提交 093f8990 编写于 作者: C ChuckStoner

EnC: MethodImpl entries should only be added if the implementing method was...

EnC: MethodImpl entries should only be added if the implementing method was added (changeset 1249535)
上级 d37d9931
......@@ -186,9 +186,10 @@ internal static class EmitHelpers
fieldsAdded,
methodsAdded,
propertiesAdded,
previousGeneration.EventMapAdded,
previousGeneration.PropertyMapAdded,
previousGeneration.TableEntriesAdded,
eventMapAdded: previousGeneration.EventMapAdded,
propertyMapAdded: previousGeneration.PropertyMapAdded,
methodImplsAdded: previousGeneration.MethodImplsAdded,
tableEntriesAdded: previousGeneration.TableEntriesAdded,
blobStreamLengthAdded: previousGeneration.BlobStreamLengthAdded,
stringStreamLengthAdded: previousGeneration.StringStreamLengthAdded,
userStringStreamLengthAdded: previousGeneration.UserStringStreamLengthAdded,
......
......@@ -127,9 +127,10 @@ internal sealed class PEDeltaAssemblyBuilder : PEAssemblyBuilderBase, IPEDeltaAs
previousGeneration.FieldsAdded,
previousGeneration.MethodsAdded,
previousGeneration.PropertiesAdded,
previousGeneration.EventMapAdded,
previousGeneration.PropertyMapAdded,
previousGeneration.TableEntriesAdded,
eventMapAdded: previousGeneration.EventMapAdded,
propertyMapAdded: previousGeneration.PropertyMapAdded,
methodImplsAdded: previousGeneration.MethodImplsAdded,
tableEntriesAdded: previousGeneration.TableEntriesAdded,
blobStreamLengthAdded: previousGeneration.BlobStreamLengthAdded,
stringStreamLengthAdded: previousGeneration.StringStreamLengthAdded,
userStringStreamLengthAdded: previousGeneration.UserStringStreamLengthAdded,
......
......@@ -863,18 +863,104 @@ class C : I
CheckEncLog(reader1,
Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default),
Row(4, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(2, TableIndex.MethodImpl, EditAndContinueOperation.Default));
Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default));
CheckEncMap(reader1,
Handle(4, TableIndex.TypeRef),
Handle(2, TableIndex.MethodDef),
Handle(2, TableIndex.AssemblyRef));
}
}
}
[Fact]
public void AddThenModifyExplicitImplementation()
{
var source0 =
@"interface I
{
void M();
}
class A : I
{
void I.M() { }
}
class B : I
{
public void M() { }
}";
var source1 =
@"interface I
{
void M();
}
class A : I
{
void I.M() { }
}
class B : I
{
public void M() { }
void I.M() { }
}";
var source2 = source1;
var compilation0 = CreateCompilationWithMscorlib(source0, compOptions: TestOptions.UnoptimizedDll);
var compilation1 = CreateCompilationWithMscorlib(source1, compOptions: TestOptions.UnoptimizedDll);
var compilation2 = CreateCompilationWithMscorlib(source2, compOptions: TestOptions.UnoptimizedDll);
var bytes0 = compilation0.EmitToArray(debug: true);
using (var md0 = ModuleMetadata.CreateFromImage(bytes0))
{
var reader0 = md0.MetadataReader;
var generation0 = EmitBaseline.CreateInitialBaseline(md0, EmptyLocalsProvider);
var method1 = compilation1.GetMember<NamedTypeSymbol>("B").GetMember<MethodSymbol>("I.M");
var diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Insert, null, method1)));
using (var block1 = diff1.GetMetadata())
{
var reader1 = block1.Reader;
var readers = new[] { reader0, reader1 };
EncValidation.VerifyModuleMvid(1, reader0, reader1);
CheckNames(readers, reader1.GetMethodDefNames(), "I.M");
CheckEncLog(reader1,
Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default),
Row(4, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(4, TableIndex.TypeDef, EditAndContinueOperation.AddMethod),
Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(2, TableIndex.MethodImpl, EditAndContinueOperation.Default));
CheckEncMap(reader1,
Handle(4, TableIndex.TypeRef),
Handle(6, TableIndex.MethodDef),
Handle(2, TableIndex.MethodImpl),
Handle(2, TableIndex.AssemblyRef));
var generation1 = diff1.NextGeneration;
var method2 = compilation2.GetMember<NamedTypeSymbol>("B").GetMember<MethodSymbol>("I.M");
var diff2 = compilation2.EmitDifference(
generation1,
ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, method1, method2)));
using (var md2 = diff2.GetMetadata())
{
var reader2 = md2.Reader;
readers = new[] { reader0, reader1, reader2 };
EncValidation.VerifyModuleMvid(2, reader1, reader2);
CheckNames(readers, reader2.GetMethodDefNames(), "I.M");
CheckEncLog(reader2,
Row(3, TableIndex.AssemblyRef, EditAndContinueOperation.Default),
Row(5, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default));
CheckEncMap(reader2,
Handle(5, TableIndex.TypeRef),
Handle(6, TableIndex.MethodDef),
Handle(3, TableIndex.AssemblyRef));
}
}
}
}
[Fact(Skip = "930065"), WorkItem(930065)]
[Fact, WorkItem(930065)]
public void ModifyConstructorBodyInPresenceOfExplicitInterfaceImplementation()
{
var source = @"
......@@ -882,22 +968,17 @@ interface I
{
void M();
}
partial class C : I
{
void I.M() { }
}
partial class C
class C : I
{
public C()
{
int a = 1;
}
void I.M() { }
}
";
var compilation0 = CreateCompilationWithMscorlib(source, compOptions: TestOptions.UnoptimizedDll);
var compilation1 = CreateCompilationWithMscorlib(source, compOptions: TestOptions.UnoptimizedDll);
// Verify full metadata contains expected rows.
var bytes0 = compilation0.EmitToArray(debug: true);
using (var md0 = ModuleMetadata.CreateFromImage(bytes0))
{
......@@ -910,7 +991,24 @@ public C()
generation0,
ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, method0, method1)));
// TODO: verify emitted metadata
using (var block1 = diff1.GetMetadata())
{
var reader1 = block1.Reader;
var readers = new[] { reader0, reader1 };
EncValidation.VerifyModuleMvid(1, reader0, reader1);
CheckNames(readers, reader1.GetTypeDefNames());
CheckNames(readers, reader1.GetMethodDefNames(), ".ctor");
CheckEncLog(reader1,
Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default),
Row(4, TableIndex.MemberRef, EditAndContinueOperation.Default),
Row(4, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default));
CheckEncMap(reader1,
Handle(4, TableIndex.TypeRef),
Handle(2, TableIndex.MethodDef),
Handle(4, TableIndex.MemberRef),
Handle(2, TableIndex.AssemblyRef));
}
}
}
......@@ -7331,7 +7429,7 @@ private static string EncLogRowToString(EditAndContinueLogEntry row)
MetadataTokens.TryGetTableIndex(row.Handle.HandleType, out tableIndex);
return string.Format(
"Row({0}, TableIndices.{1}, EditAndContinueOperation.{2})",
"Row({0}, TableIndex.{1}, EditAndContinueOperation.{2})",
MetadataTokens.GetRowNumber(row.Handle),
tableIndex,
row.Operation);
......@@ -7343,7 +7441,7 @@ private static string EncMapRowToString(Handle handle)
MetadataTokens.TryGetTableIndex(handle.HandleType, out tableIndex);
return string.Format(
"Handle({0}, TableIndices.{1})",
"Handle({0}, TableIndex.{1})",
MetadataTokens.GetRowNumber(handle),
tableIndex);
}
......@@ -7355,7 +7453,7 @@ private static string AttributeRowToString(CustomAttributeRow row)
MetadataTokens.TryGetTableIndex(row.ConstructorToken.HandleType, out constructorTableIndex);
return string.Format(
"new CustomAttributeRow(Handle({0}, TableIndices.{1}), Handle({2}, TableIndices.{3}))",
"new CustomAttributeRow(Handle({0}, TableIndex.{1}), Handle({2}, TableIndex.{3}))",
MetadataTokens.GetRowNumber(row.ParentToken),
parentTableIndex,
MetadataTokens.GetRowNumber(row.ConstructorToken),
......
......@@ -32,6 +32,7 @@ internal sealed class DeltaPeWriter : PeWriter
private readonly GenericParameterIndex genericParameters;
private readonly EventOrPropertyMapIndex eventMap;
private readonly EventOrPropertyMapIndex propertyMap;
private readonly MethodImplIndex methodImpls;
private readonly HeapOrReferenceIndex<IAssemblyReference> assemblyRefIndex;
private readonly HeapOrReferenceIndex<string> moduleRefIndex;
......@@ -78,6 +79,7 @@ internal sealed class DeltaPeWriter : PeWriter
this.genericParameters = new GenericParameterIndex((uint)sizes[(int)TableIndex.GenericParam]);
this.eventMap = new EventOrPropertyMapIndex(this.TryGetExistingEventMapIndex, (uint)sizes[(int)TableIndex.EventMap]);
this.propertyMap = new EventOrPropertyMapIndex(this.TryGetExistingPropertyMapIndex, (uint)sizes[(int)TableIndex.PropertyMap]);
this.methodImpls = new MethodImplIndex(this, (uint)sizes[(int)TableIndex.MethodImpl]);
this.assemblyRefIndex = new HeapOrReferenceIndex<IAssemblyReference>(this, AssemblyReferenceComparer.Instance, lastRowId: (uint)sizes[(int)TableIndex.AssemblyRef]);
this.moduleRefIndex = new HeapOrReferenceIndex<string>(this, lastRowId: (uint)sizes[(int)TableIndex.ModuleRef]);
......@@ -106,6 +108,7 @@ private ImmutableArray<int> GetDeltaTableSizes()
sizes[(int)TableIndex.Event] = this.eventDefs.GetAdded().Count;
sizes[(int)TableIndex.PropertyMap] = this.propertyMap.GetAdded().Count;
sizes[(int)TableIndex.Property] = this.propertyDefs.GetAdded().Count;
sizes[(int)TableIndex.MethodImpl] = this.methodImpls.GetAdded().Count;
sizes[(int)TableIndex.ModuleRef] = this.moduleRefIndex.Rows.Count;
sizes[(int)TableIndex.TypeSpec] = this.typeSpecIndex.Rows.Count;
sizes[(int)TableIndex.AssemblyRef] = this.assemblyRefIndex.Rows.Count;
......@@ -153,6 +156,7 @@ private ImmutableArray<int> GetDeltaTableSizes()
propertiesAdded: AddRange(this.previousGeneration.PropertiesAdded, this.propertyDefs.GetAdded()),
eventMapAdded: AddRange(this.previousGeneration.EventMapAdded, this.eventMap.GetAdded()),
propertyMapAdded: AddRange(this.previousGeneration.PropertyMapAdded, this.propertyMap.GetAdded()),
methodImplsAdded: AddRange(this.previousGeneration.MethodImplsAdded, this.methodImpls.GetAdded()),
tableEntriesAdded: ImmutableArray.Create(tableSizes),
// Blob stream is concatenated aligned.
blobStreamLengthAdded: (int)this.blobWriter.BaseStream.Length + this.previousGeneration.BlobStreamLengthAdded,
......@@ -410,19 +414,38 @@ protected override IReadOnlyList<uint> GetStandAloneSignatures()
return this.standAloneSignatureIndex.Rows;
}
protected override IEnumerable<INamespaceTypeDefinition> GetTopLevelTypes(IModule module)
private IEnumerable<INamespaceTypeDefinition> GetTopLevelTypes()
{
return this.changes.GetTopLevelTypes(this.Context);
}
protected override void CreateIndicesForModule()
{
base.CreateIndicesForModule();
var typeDefs = ArrayBuilder<ITypeDefinition>.GetInstance();
this.GetTypesAndNestedTypes(typeDefs, this.GetTopLevelTypes());
foreach (var typeDef in typeDefs)
{
this.CreateIndicesForNonTypeMembers(typeDef);
}
typeDefs.Free();
var module = (IPEDeltaAssemblyBuilder)this.module;
module.OnCreatedIndices(this.Context.Diagnostics);
}
protected override void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef)
/// <summary>
/// Get the set of types and nested types, enclosing types first.
/// </summary>
private void GetTypesAndNestedTypes(ArrayBuilder<ITypeDefinition> builder, IEnumerable<ITypeDefinition> typeDefs)
{
foreach (var typeDef in typeDefs)
{
builder.Add(typeDef);
GetTypesAndNestedTypes(builder, typeDef.GetNestedTypes(this.Context));
}
}
private void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef)
{
switch (this.changes.GetChange(typeDef))
{
......@@ -449,11 +472,6 @@ protected override void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef)
var ok = this.typeDefs.TryGetValue(typeDef, out typeIndex);
Debug.Assert(ok);
foreach (var methodImpl in typeDef.GetExplicitImplementationOverrides(Context))
{
this.methodImplList.Add(methodImpl);
}
foreach (var eventDef in typeDef.Events)
{
uint eventMapIndex;
......@@ -500,6 +518,46 @@ protected override void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef)
this.AddDefIfNecessary(this.propertyDefs, propertyDef);
}
var implementingMethods = ArrayBuilder<uint>.GetInstance();
// First, visit all IMethodImplementations and add to this.methodImplList.
foreach (var methodImpl in typeDef.GetExplicitImplementationOverrides(Context))
{
var methodDef = (IMethodDefinition)methodImpl.ImplementingMethod.AsDefinition(this.Context);
uint methodDefIndex;
ok = this.methodDefs.TryGetValue(methodDef, out methodDefIndex);
Debug.Assert(ok);
// If there are N existing MethodImpl entries for this MethodDef,
// those will be index:1, ..., index:N, so it's sufficient to check for index:1.
uint methodImplIndex;
var key = new MethodImplKey(methodDefIndex, index: 1);
if (!this.methodImpls.TryGetValue(key, out methodImplIndex))
{
implementingMethods.Add(methodDefIndex);
this.methodImplList.Add(methodImpl);
}
}
// Next, add placeholders to this.methodImpls for items added above.
foreach (var methodDefIndex in implementingMethods)
{
int index = 1;
while (true)
{
uint methodImplIndex;
var key = new MethodImplKey(methodDefIndex, index);
if (!this.methodImpls.TryGetValue(key, out methodImplIndex))
{
this.methodImpls.Add(key);
break;
}
index++;
}
}
implementingMethods.Free();
}
private bool AddDefIfNecessary<T>(DefinitionIndex<T> defIndex, T def)
......@@ -777,7 +835,7 @@ protected override void PopulateEncMapTableRows(List<EncMapRow> table)
// for <PrivateImplementationDetails> and that class is not used in ENC.
// If we need FieldRva in the future, we'll need a corresponding test.
// (See EditAndContinueTests.FieldRva that was deleted in this change.)
//TableIndices.FieldRva,
//TableIndex.FieldRva,
TableIndex.EncLog,
TableIndex.EncMap,
TableIndex.Assembly,
......@@ -1108,6 +1166,48 @@ private bool TryGetExistingPropertyDefIndex(IPropertyDefinition item, out uint i
return false;
}
private bool TryGetExistingEventMapIndex(uint item, out uint index)
{
if (this.previousGeneration.EventMapAdded.TryGetValue(item, out index))
{
return true;
}
if (this.previousGeneration.TypeToEventMap.TryGetValue(item, out index))
{
return true;
}
index = 0;
return false;
}
private bool TryGetExistingPropertyMapIndex(uint item, out uint index)
{
if (this.previousGeneration.PropertyMapAdded.TryGetValue(item, out index))
{
return true;
}
if (this.previousGeneration.TypeToPropertyMap.TryGetValue(item, out index))
{
return true;
}
index = 0;
return false;
}
private bool TryGetExistingMethodImplIndex(MethodImplKey item, out uint index)
{
if (this.previousGeneration.MethodImplsAdded.TryGetValue(item, out index))
{
return true;
}
if (this.previousGeneration.MethodImpls.TryGetValue(item, out index))
{
return true;
}
index = 0;
return false;
}
private sealed class ParameterDefinitionIndex : DefinitionIndexBase<IParameterDefinition>
{
public ParameterDefinitionIndex(uint lastRowId) :
......@@ -1188,32 +1288,38 @@ public void Add(uint item)
}
}
private bool TryGetExistingEventMapIndex(uint item, out uint index)
private sealed class MethodImplIndex : DefinitionIndexBase<MethodImplKey>
{
if (this.previousGeneration.EventMapAdded.TryGetValue(item, out index))
{
return true;
}
if (this.previousGeneration.TypeToEventMap.TryGetValue(item, out index))
private readonly DeltaPeWriter writer;
public MethodImplIndex(DeltaPeWriter writer, uint lastRowId) :
base(lastRowId)
{
return true;
this.writer = writer;
}
index = 0;
return false;
}
private bool TryGetExistingPropertyMapIndex(uint item, out uint index)
{
if (this.previousGeneration.PropertyMapAdded.TryGetValue(item, out index))
public override bool TryGetValue(MethodImplKey item, out uint index)
{
return true;
if (this.added.TryGetValue(item, out index))
{
return true;
}
if (this.writer.TryGetExistingMethodImplIndex(item, out index))
{
return true;
}
index = 0;
return false;
}
if (this.previousGeneration.TypeToPropertyMap.TryGetValue(item, out index))
public void Add(MethodImplKey item)
{
return true;
Debug.Assert(!this.IsFrozen);
uint index = this.NextRowId;
this.added.Add(item, index);
this.rows.Add(item);
}
index = 0;
return false;
}
private sealed class DeltaReferenceIndexer : ReferenceIndexer
......@@ -1234,7 +1340,7 @@ public override void Visit(IAssembly assembly)
public override void Visit(IModule module)
{
this.module = module;
this.Visit(((DeltaPeWriter)this.peWriter).GetTopLevelTypes(module));
this.Visit(((DeltaPeWriter)this.peWriter).GetTopLevelTypes());
}
public override void Visit(IEventDefinition eventDefinition)
......@@ -1257,8 +1363,13 @@ public override void Visit(IMethodDefinition method)
public override void Visit(IMethodImplementation methodImplementation)
{
Debug.Assert(this.ShouldVisit((IMethodDefinition)methodImplementation.ImplementingMethod));
base.Visit(methodImplementation);
// Unless the implementing method was added,
// the method implementation already exists.
var methodDef = (IMethodDefinition)methodImplementation.ImplementingMethod.AsDefinition(this.Context);
if (this.changes.GetChange(methodDef) == SymbolChange.Added)
{
base.Visit(methodImplementation);
}
}
public override void Visit(INamespaceTypeDefinition namespaceTypeDefinition)
......
......@@ -7,11 +7,45 @@
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using Microsoft.Cci;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Emit
{
public delegate ImmutableArray<string> LocalVariableNameProvider(uint methodIndex);
// A MethodImpl entry is a pair of implementing method and implemented
// method. However, the implemented method is a MemberRef rather
// than a MethodDef (e.g.: I<int>.M) and currently we are not mapping
// MemberRefs between generations so it's not possible to track the
// implemented method. Instead, recognizing that we do not support
// changes to the set of implemented methods for a particular MethodDef,
// and that we do not use the implementing methods anywhere, it's
// sufficient to track a pair of implementing method and index.
internal struct MethodImplKey : IEquatable<MethodImplKey>
{
internal MethodImplKey(uint implementingMethod, int index)
{
Debug.Assert(implementingMethod > 0);
Debug.Assert(index > 0);
this.ImplementingMethod = implementingMethod;
this.Index = index;
}
internal readonly uint ImplementingMethod;
internal readonly int Index;
public bool Equals(MethodImplKey other)
{
return this.ImplementingMethod == other.ImplementingMethod &&
this.Index == other.Index;
}
public override int GetHashCode()
{
return Hash.Combine((int)this.ImplementingMethod, this.Index);
}
}
/// <summary>
/// Represents a module from a previous compilation. Used in Edit and Continue
/// to emit the differences in a subsequent compilation.
......@@ -62,14 +96,6 @@ public static EmitBaseline CreateInitialBaseline(ModuleMetadata module, LocalVar
throw new ArgumentNullException("localNames");
}
return CreateInitialBaselineWithoutChecks(module, localNames);
}
/// <summary>
/// Entrypoint for the <see cref="T:Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.CSharpExpressionCompiler"/>.
/// </summary>
internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata module, LocalVariableNameProvider localNames)
{
var reader = module.MetadataReader;
var moduleVersionId = module.GetModuleVersionId();
......@@ -87,6 +113,7 @@ internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata m
propertiesAdded: new Dictionary<IPropertyDefinition, uint>(),
eventMapAdded: new Dictionary<uint, uint>(),
propertyMapAdded: new Dictionary<uint, uint>(),
methodImplsAdded: new Dictionary<MethodImplKey, uint>(),
tableEntriesAdded: EmptyTableSizes,
blobStreamLengthAdded: 0,
stringStreamLengthAdded: 0,
......@@ -96,7 +123,8 @@ internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata m
localsForMethodsAddedOrChanged: new Dictionary<uint, ImmutableArray<EncLocalInfo>>(),
localNames: localNames,
typeToEventMap: reader.CalculateTypeEventMap(),
typeToPropertyMap: reader.CalculateTypePropertyMap());
typeToPropertyMap: reader.CalculateTypePropertyMap(),
methodImpls: CalculateMethodImpls(reader));
}
/// <summary>
......@@ -127,6 +155,7 @@ internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata m
internal readonly IReadOnlyDictionary<IPropertyDefinition, uint> PropertiesAdded;
internal readonly IReadOnlyDictionary<uint, uint> EventMapAdded;
internal readonly IReadOnlyDictionary<uint, uint> PropertyMapAdded;
internal readonly IReadOnlyDictionary<MethodImplKey, uint> MethodImplsAdded;
internal readonly ImmutableArray<int> TableEntriesAdded;
......@@ -150,6 +179,7 @@ internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata m
internal readonly ImmutableArray<int> TableSizes;
internal readonly IReadOnlyDictionary<uint, uint> TypeToEventMap;
internal readonly IReadOnlyDictionary<uint, uint> TypeToPropertyMap;
internal readonly IReadOnlyDictionary<MethodImplKey, uint> MethodImpls;
internal readonly IReadOnlyDictionary<AnonymousTypeKey, AnonymousTypeValue> AnonymousTypeMap;
private EmitBaseline(
......@@ -166,6 +196,7 @@ internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata m
IReadOnlyDictionary<IPropertyDefinition, uint> propertiesAdded,
IReadOnlyDictionary<uint, uint> eventMapAdded,
IReadOnlyDictionary<uint, uint> propertyMapAdded,
IReadOnlyDictionary<MethodImplKey, uint> methodImplsAdded,
ImmutableArray<int> tableEntriesAdded,
int blobStreamLengthAdded,
int stringStreamLengthAdded,
......@@ -175,7 +206,8 @@ internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata m
IReadOnlyDictionary<uint, ImmutableArray<EncLocalInfo>> localsForMethodsAddedOrChanged,
LocalVariableNameProvider localNames,
IReadOnlyDictionary<uint, uint> typeToEventMap,
IReadOnlyDictionary<uint, uint> typeToPropertyMap)
IReadOnlyDictionary<uint, uint> typeToPropertyMap,
IReadOnlyDictionary<MethodImplKey, uint> methodImpls)
{
Debug.Assert(module != null);
Debug.Assert((ordinal == 0) == (encId == default(Guid)));
......@@ -216,6 +248,7 @@ internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata m
this.PropertiesAdded = propertiesAdded;
this.EventMapAdded = eventMapAdded;
this.PropertyMapAdded = propertyMapAdded;
this.MethodImplsAdded = methodImplsAdded;
this.TableEntriesAdded = tableEntriesAdded;
this.BlobStreamLengthAdded = blobStreamLengthAdded;
this.StringStreamLengthAdded = stringStreamLengthAdded;
......@@ -228,6 +261,7 @@ internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata m
this.TableSizes = CalculateTableSizes(reader, this.TableEntriesAdded);
this.TypeToEventMap = typeToEventMap;
this.TypeToPropertyMap = typeToPropertyMap;
this.MethodImpls = methodImpls;
}
internal EmitBaseline With(
......@@ -242,6 +276,7 @@ internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata m
IReadOnlyDictionary<IPropertyDefinition, uint> propertiesAdded,
IReadOnlyDictionary<uint, uint> eventMapAdded,
IReadOnlyDictionary<uint, uint> propertyMapAdded,
IReadOnlyDictionary<MethodImplKey, uint> methodImplsAdded,
ImmutableArray<int> tableEntriesAdded,
int blobStreamLengthAdded,
int stringStreamLengthAdded,
......@@ -268,6 +303,7 @@ internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata m
propertiesAdded,
eventMapAdded,
propertyMapAdded,
methodImplsAdded,
tableEntriesAdded,
blobStreamLengthAdded: blobStreamLengthAdded,
stringStreamLengthAdded: stringStreamLengthAdded,
......@@ -277,7 +313,8 @@ internal static EmitBaseline CreateInitialBaselineWithoutChecks(ModuleMetadata m
localsForMethodsAddedOrChanged: localsForMethodsAddedOrChanged,
localNames: localNames,
typeToEventMap: this.TypeToEventMap,
typeToPropertyMap: this.TypeToPropertyMap);
typeToPropertyMap: this.TypeToPropertyMap,
methodImpls: this.MethodImpls);
}
internal MetadataReader MetadataReader
......@@ -317,6 +354,34 @@ private static ImmutableArray<int> CalculateTableSizes(MetadataReader reader, Im
return ImmutableArray.Create(sizes);
}
private static Dictionary<MethodImplKey, uint> CalculateMethodImpls(MetadataReader reader)
{
var result = new Dictionary<MethodImplKey, uint>();
int n = reader.GetTableRowCount(TableIndex.MethodImpl);
for (int row = 1; row <= n; row++)
{
var methodImpl = reader.GetMethodImplementation(MetadataTokens.MethodImplementationHandle(row));
// Hold on to the implementing method def but use a simple
// index for the implemented method ref token. (We do not map
// member refs currently, and since we don't allow changes to
// the set of methods a method def implements, the actual
// tokens of the implemented methods are not needed.)
var methodDefRow = (uint)MetadataTokens.GetRowNumber(methodImpl.MethodBody);
int index = 1;
while (true)
{
var key = new MethodImplKey(methodDefRow, index);
if (!result.ContainsKey(key))
{
result.Add(key, (uint)row);
break;
}
index++;
}
}
return result;
}
internal int GetNextAnonymousTypeIndex(bool fromDelegates = false)
{
int nextIndex = 0;
......
......@@ -347,12 +347,37 @@ protected override void PopulatePropertyMapTableRows(List<PropertyMapRow> table)
}
}
protected override IEnumerable<INamespaceTypeDefinition> GetTopLevelTypes(IModule module)
protected override void CreateIndicesForModule()
{
return module.GetTopLevelTypes(this.Context);
var nestedTypes = new Queue<ITypeDefinition>();
foreach (INamespaceTypeDefinition typeDef in this.module.GetTopLevelTypes(this.Context))
{
this.CreateIndicesFor(typeDef, nestedTypes);
}
while (nestedTypes.Count > 0)
{
this.CreateIndicesFor(nestedTypes.Dequeue(), nestedTypes);
}
}
private void CreateIndicesFor(ITypeDefinition typeDef, Queue<ITypeDefinition> nestedTypes)
{
this.cancellationToken.ThrowIfCancellationRequested();
this.CreateIndicesForNonTypeMembers(typeDef);
// Metadata spec:
// The TypeDef table has a special ordering constraint:
// the definition of an enclosing class shall precede the definition of all classes it encloses.
foreach (var nestedType in typeDef.GetNestedTypes(this.Context))
{
nestedTypes.Enqueue(nestedType);
}
}
protected override void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef)
private void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef)
{
this.typeDefs.Add(typeDef);
......
......@@ -386,10 +386,6 @@ protected Guid ModuleVersionId
/// </summary>
protected abstract IReadOnlyList<uint> GetStandAloneSignatures();
protected abstract IEnumerable<INamespaceTypeDefinition> GetTopLevelTypes(IModule module);
protected abstract void CreateIndicesForNonTypeMembers(ITypeDefinition typeDef);
/// <summary>
/// Offset into full metadata blob stream.
/// </summary>
......@@ -447,7 +443,7 @@ protected virtual void OnSerializedMetadataTables()
// If true, it is allowed to have methods not have bodies (for emitting metadata-only
// assembly)
internal readonly bool allowMissingMethodBodies;
private readonly CancellationToken cancellationToken;
protected readonly CancellationToken cancellationToken;
protected readonly IModule module;
public readonly EmitContext Context;
private readonly CommonMessageProvider messageProvider;
......@@ -1093,35 +1089,7 @@ private void CreateUserStringIndices()
}
}
protected virtual void CreateIndicesForModule()
{
var nestedTypes = new Queue<ITypeDefinition>();
foreach (INamespaceTypeDefinition typeDef in this.GetTopLevelTypes(this.module))
{
this.CreateIndicesFor(typeDef, nestedTypes);
}
while (nestedTypes.Count > 0)
{
this.CreateIndicesFor(nestedTypes.Dequeue(), nestedTypes);
}
}
private void CreateIndicesFor(ITypeDefinition typeDef, Queue<ITypeDefinition> nestedTypes)
{
this.cancellationToken.ThrowIfCancellationRequested();
this.CreateIndicesForNonTypeMembers(typeDef);
// Metadata spec:
// The TypeDef table has a special ordering constraint:
// the definition of an enclosing class shall precede the definition of all classes it encloses.
foreach (var nestedType in typeDef.GetNestedTypes(Context))
{
nestedTypes.Enqueue(nestedType);
}
}
protected abstract void CreateIndicesForModule();
protected IEnumerable<IGenericTypeParameter> GetConsolidatedTypeParameters(ITypeDefinition typeDef)
{
......
......@@ -163,9 +163,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit
fieldsAdded,
methodsAdded,
propertiesAdded,
previousGeneration.EventMapAdded,
previousGeneration.PropertyMapAdded,
previousGeneration.TableEntriesAdded,
eventMapAdded:=previousGeneration.EventMapAdded,
propertyMapAdded:=previousGeneration.PropertyMapAdded,
methodImplsAdded:=previousGeneration.MethodImplsAdded,
tableEntriesAdded:=previousGeneration.TableEntriesAdded,
blobStreamLengthAdded:=previousGeneration.BlobStreamLengthAdded,
stringStreamLengthAdded:=previousGeneration.StringStreamLengthAdded,
userStringStreamLengthAdded:=previousGeneration.UserStringStreamLengthAdded,
......
......@@ -144,9 +144,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit
previousGeneration.FieldsAdded,
previousGeneration.MethodsAdded,
previousGeneration.PropertiesAdded,
previousGeneration.EventMapAdded,
previousGeneration.PropertyMapAdded,
previousGeneration.TableEntriesAdded,
eventMapAdded:=previousGeneration.EventMapAdded,
propertyMapAdded:=previousGeneration.PropertyMapAdded,
methodImplsAdded:=previousGeneration.MethodImplsAdded,
tableEntriesAdded:=previousGeneration.TableEntriesAdded,
blobStreamLengthAdded:=previousGeneration.BlobStreamLengthAdded,
stringStreamLengthAdded:=previousGeneration.StringStreamLengthAdded,
userStringStreamLengthAdded:=previousGeneration.UserStringStreamLengthAdded,
......
......@@ -3,6 +3,7 @@
Imports System.Collections.Immutable
Imports System.IO
Imports System.Reflection.Metadata
Imports System.Reflection.Metadata.Ecma335
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeGen
Imports Microsoft.CodeAnalysis.Emit
......@@ -17,6 +18,221 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests
Public Class EditAndContinueTests
Inherits BasicTestBase
<Fact>
Public Sub AddThenModifyExplicitImplementation()
Dim source0 =
<compilation>
<file name="a.vb">
Interface I(Of T)
Sub M()
End Interface
</file>
</compilation>
Dim source1 =
<compilation>
<file name="a.vb">
Interface I(Of T)
Sub M()
End Interface
Class A
Implements I(Of Integer), I(Of Object)
Public Sub New()
End Sub
Sub M() Implements I(Of Integer).M, I(Of Object).M
End Sub
End Class
</file>
</compilation>
Dim source2 = source1
Dim source3 =
<compilation>
<file name="a.vb">
Interface I(Of T)
Sub M()
End Interface
Class A
Implements I(Of Integer), I(Of Object)
Public Sub New()
End Sub
Sub M() Implements I(Of Integer).M, I(Of Object).M
End Sub
End Class
Class B
Implements I(Of Object)
Public Sub New()
End Sub
Sub M() Implements I(Of Object).M
End Sub
End Class
</file>
</compilation>
Dim compilation0 = CreateCompilationWithMscorlibAndVBRuntime(source0, UnoptimizedDll)
Dim compilation1 = CreateCompilationWithMscorlibAndVBRuntime(source1, UnoptimizedDll)
Dim compilation2 = CreateCompilationWithMscorlibAndVBRuntime(source2, UnoptimizedDll)
Dim compilation3 = CreateCompilationWithMscorlibAndVBRuntime(source3, UnoptimizedDll)
Dim bytes0 = compilation0.EmitToArray(debug:=True)
Using md0 = ModuleMetadata.CreateFromImage(bytes0)
Dim reader0 = md0.MetadataReader
Dim type1 = compilation1.GetMember(Of NamedTypeSymbol)("A")
Dim method1 = compilation1.GetMember(Of MethodSymbol)("A.M")
Dim generation0 = EmitBaseline.CreateInitialBaseline(ModuleMetadata.CreateFromImage(bytes0), EmptyLocalsProvider)
Dim diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Insert, Nothing, type1)))
Using md1 = diff1.GetMetadata()
Dim reader1 = md1.Reader
Dim readers = {reader0, reader1}
CheckNames(readers, reader1.GetMethodDefNames(), ".ctor", "M")
CheckEncLog(reader1,
Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default),
Row(3, TableIndex.MemberRef, EditAndContinueOperation.Default),
Row(4, TableIndex.MemberRef, EditAndContinueOperation.Default),
Row(5, TableIndex.MemberRef, EditAndContinueOperation.Default),
Row(3, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(1, TableIndex.TypeSpec, EditAndContinueOperation.Default),
Row(2, TableIndex.TypeSpec, EditAndContinueOperation.Default),
Row(3, TableIndex.TypeDef, EditAndContinueOperation.Default),
Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddMethod),
Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddMethod),
Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(1, TableIndex.MethodImpl, EditAndContinueOperation.Default),
Row(2, TableIndex.MethodImpl, EditAndContinueOperation.Default),
Row(1, TableIndex.InterfaceImpl, EditAndContinueOperation.Default),
Row(2, TableIndex.InterfaceImpl, EditAndContinueOperation.Default))
CheckEncMap(reader1,
Handle(3, TableIndex.TypeRef),
Handle(3, TableIndex.TypeDef),
Handle(2, TableIndex.MethodDef),
Handle(3, TableIndex.MethodDef),
Handle(1, TableIndex.InterfaceImpl),
Handle(2, TableIndex.InterfaceImpl),
Handle(3, TableIndex.MemberRef),
Handle(4, TableIndex.MemberRef),
Handle(5, TableIndex.MemberRef),
Handle(1, TableIndex.MethodImpl),
Handle(2, TableIndex.MethodImpl),
Handle(1, TableIndex.TypeSpec),
Handle(2, TableIndex.TypeSpec),
Handle(2, TableIndex.AssemblyRef))
Dim generation1 = diff1.NextGeneration
Dim method2 = compilation2.GetMember(Of MethodSymbol)("A.M")
Dim diff2 = compilation2.EmitDifference(
generation1,
ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, method1, method2)))
Using md2 = diff2.GetMetadata()
Dim reader2 = md2.Reader
readers = {reader0, reader1, reader2}
CheckNames(readers, reader2.GetMethodDefNames(), "M")
CheckEncLog(reader2,
Row(3, TableIndex.AssemblyRef, EditAndContinueOperation.Default),
Row(4, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(3, TableIndex.TypeSpec, EditAndContinueOperation.Default),
Row(4, TableIndex.TypeSpec, EditAndContinueOperation.Default),
Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default))
CheckEncMap(reader2,
Handle(4, TableIndex.TypeRef),
Handle(3, TableIndex.MethodDef),
Handle(3, TableIndex.TypeSpec),
Handle(4, TableIndex.TypeSpec),
Handle(3, TableIndex.AssemblyRef))
Dim generation2 = diff2.NextGeneration
Dim type3 = compilation3.GetMember(Of NamedTypeSymbol)("B")
Dim diff3 = compilation3.EmitDifference(
generation1,
ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Insert, Nothing, type3)))
Using md3 = diff3.GetMetadata()
Dim reader3 = md3.Reader
readers = {reader0, reader1, reader3}
CheckNames(readers, reader3.GetMethodDefNames(), ".ctor", "M")
CheckEncLog(reader3,
Row(3, TableIndex.AssemblyRef, EditAndContinueOperation.Default),
Row(6, TableIndex.MemberRef, EditAndContinueOperation.Default),
Row(7, TableIndex.MemberRef, EditAndContinueOperation.Default),
Row(4, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(3, TableIndex.TypeSpec, EditAndContinueOperation.Default),
Row(4, TableIndex.TypeDef, EditAndContinueOperation.Default),
Row(4, TableIndex.TypeDef, EditAndContinueOperation.AddMethod),
Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(4, TableIndex.TypeDef, EditAndContinueOperation.AddMethod),
Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(3, TableIndex.MethodImpl, EditAndContinueOperation.Default),
Row(3, TableIndex.InterfaceImpl, EditAndContinueOperation.Default))
CheckEncMap(reader3,
Handle(4, TableIndex.TypeRef),
Handle(4, TableIndex.TypeDef),
Handle(4, TableIndex.MethodDef),
Handle(5, TableIndex.MethodDef),
Handle(3, TableIndex.InterfaceImpl),
Handle(6, TableIndex.MemberRef),
Handle(7, TableIndex.MemberRef),
Handle(3, TableIndex.MethodImpl),
Handle(3, TableIndex.TypeSpec),
Handle(3, TableIndex.AssemblyRef))
End Using
End Using
End Using
End Using
End Sub
<Fact, WorkItem(930065)>
Public Sub ModifyConstructorBodyInPresenceOfExplicitInterfaceImplementation()
Dim source =
<compilation>
<file name="a.vb">
Interface I
Sub M1()
Sub M2()
End Interface
Class C
Implements I
Public Sub New()
End Sub
Sub M() Implements I.M1, I.M2
End Sub
End Class
</file>
</compilation>
Dim compilation0 = CreateCompilationWithMscorlibAndVBRuntime(source, UnoptimizedDll)
Dim compilation1 = CreateCompilationWithMscorlibAndVBRuntime(source, UnoptimizedDll)
Dim bytes0 = compilation0.EmitToArray(debug:=True)
Using md0 = ModuleMetadata.CreateFromImage(bytes0)
Dim reader0 = md0.MetadataReader
Dim method0 = compilation0.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single()
Dim method1 = compilation1.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single()
Dim generation0 = EmitBaseline.CreateInitialBaseline(ModuleMetadata.CreateFromImage(bytes0), EmptyLocalsProvider)
Dim diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, method0, method1)))
Using md1 = diff1.GetMetadata()
Dim reader1 = md1.Reader
Dim readers = {reader0, reader1}
CheckNames(readers, reader1.GetTypeDefNames())
CheckNames(readers, reader1.GetMethodDefNames(), ".ctor")
CheckEncLog(reader1,
Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default),
Row(4, TableIndex.MemberRef, EditAndContinueOperation.Default),
Row(4, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default))
CheckEncMap(reader1,
Handle(4, TableIndex.TypeRef),
Handle(3, TableIndex.MethodDef),
Handle(4, TableIndex.MemberRef),
Handle(2, TableIndex.AssemblyRef))
End Using
End Using
End Sub
<Fact>
Public Sub NamespacesAndOverloads()
Dim compilation0 = CreateCompilationWithMscorlibAndVBRuntime(options:=UnoptimizedDll, sources:=
......@@ -759,11 +975,9 @@ End Module</file>
Dim prop0 = compilation0.GetMember(Of PropertySymbol)("Module1.GetName")
Dim prop1 = compilation1.GetMember(Of PropertySymbol)("Module1.GetName")
Dim method1 = prop1.SetMethod
Dim getLocalNamesFunc As LocalVariableNameProvider = Function(m) ImmutableArray(Of String).Empty
Dim generation0 = EmitBaseline.CreateInitialBaseline(ModuleMetadata.CreateFromImage(bytes0), getLocalNamesFunc)
Dim generation0 = EmitBaseline.CreateInitialBaseline(ModuleMetadata.CreateFromImage(bytes0), EmptyLocalsProvider)
Dim diff1 = compilation1.EmitDifference(
generation0,
......@@ -3559,7 +3773,7 @@ End Module
Dim bytes0 = compilation0.EmitToArray(debug:=True)
Dim method0 = compilation0.GetMember(Of MethodSymbol)("M.F")
Dim method1 = compilation1.GetMember(Of MethodSymbol)("M.F")
Dim generation0 = EmitBaseline.CreateInitialBaseline(ModuleMetadata.CreateFromImage(bytes0), Function(m) ImmutableArray(Of String).Empty)
Dim generation0 = EmitBaseline.CreateInitialBaseline(ModuleMetadata.CreateFromImage(bytes0), EmptyLocalsProvider)
compilation1.EmitDifference(
generation0,
ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, method0, method1)))
......@@ -3595,13 +3809,13 @@ End Module
Dim bytes0 = compilation0.EmitToArray(debug:=True)
Dim method0 = compilation0.GetMember(Of MethodSymbol)("M.F")
Dim method1 = compilation1.GetMember(Of MethodSymbol)("M.F")
Dim generation0 = EmitBaseline.CreateInitialBaseline(ModuleMetadata.CreateFromImage(bytes0), Function(m) ImmutableArray(Of String).Empty)
Dim generation0 = EmitBaseline.CreateInitialBaseline(ModuleMetadata.CreateFromImage(bytes0), EmptyLocalsProvider)
Dim diff0 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, method0, method1, GetLocalMap(method1, method0), preserveLocalVariables:=True)))
diff0.VerifyIL("
diff0.VerifyIL(<![CDATA[
{
// Code size 13 (0xd)
.maxstack 3
......@@ -3614,33 +3828,38 @@ End Module
IL_000a: ldc.i4.3
IL_000b: stelem.i4
IL_000c: ret
}")
}
]]>.Value)
End Sub
#End Region
<Fact>
Public Sub SymWriterErrors()
Dim source0 = "
Class C
End Class
"
Dim source1 = "
Class C
Sub Main
End Sub
End Class
"
Dim compilation0 = CreateCompilationWithMscorlib({source0}, compOptions:=Options.UnoptimizedDll)
Dim compilation1 = CreateCompilationWithMscorlib({source1}, compOptions:=Options.UnoptimizedDll)
Dim source0 =
<compilation>
<file name="a.vb"><![CDATA[
Class C
End Class
]]></file>
</compilation>
Dim source1 =
<compilation>
<file name="a.vb"><![CDATA[
Class C
Sub Main()
End Sub
End Class
]]></file>
</compilation>
Dim compilation0 = CreateCompilationWithMscorlib(source0, Options.UnoptimizedDll)
Dim compilation1 = CreateCompilationWithMscorlib(source1, Options.UnoptimizedDll)
' Verify full metadata contains expected rows.
Dim bytes0 = compilation0.EmitToArray(debug:=True)
Using md0 = ModuleMetadata.CreateFromImage(bytes0)
Using md0 = ModuleMetadata.CreateFromImage(bytes0)
Dim diff1 = compilation1.EmitDifference(
Dim diff1 = compilation1.EmitDifference(
EmitBaseline.CreateInitialBaseline(md0, EmptyLocalsProvider),
ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Insert, Nothing, compilation1.GetMember(Of MethodSymbol)("C.Main"))),
New CompilationTestData With {.SymWriterFactory = Function() New MockSymUnmanagedWriter()})
......@@ -3652,7 +3871,6 @@ End Class
End Using
End Sub
#Region "Helpers"
Private Shared ReadOnly EmptyLocalsProvider As LocalVariableNameProvider = Function(token) ImmutableArray(Of String).Empty
......@@ -3739,6 +3957,22 @@ End Class
Return Nothing
End Function
Private Shared Function Row(rowNumber As Integer, table As TableIndex, operation As EditAndContinueOperation) As EditAndContinueLogEntry
Return New EditAndContinueLogEntry(MetadataTokens.Handle(table, rowNumber), operation)
End Function
Private Shared Function Handle(rowNumber As Integer, table As TableIndex) As Handle
Return MetadataTokens.Handle(table, rowNumber)
End Function
Private Shared Sub CheckEncLog(reader As MetadataReader, ParamArray rows As EditAndContinueLogEntry())
AssertEx.Equal(rows, reader.GetEditAndContinueLogEntries(), itemInspector:=AddressOf EncLogRowToString)
End Sub
Private Shared Sub CheckEncMap(reader As MetadataReader, ParamArray [handles] As Handle())
AssertEx.Equal([handles], reader.GetEditAndContinueMapEntries(), itemInspector:=AddressOf EncMapRowToString)
End Sub
Private Shared Sub CheckNames(reader As MetadataReader, [handles] As StringHandle(), ParamArray expectedNames As String())
CheckNames({reader}, [handles], expectedNames)
End Sub
......@@ -3755,6 +3989,25 @@ End Class
AssertEx.Equal(actualNames, expectedNames)
End Sub
Private Shared Function EncLogRowToString(row As EditAndContinueLogEntry) As String
Dim index As TableIndex = 0
MetadataTokens.TryGetTableIndex(row.Handle.HandleType, index)
Return String.Format(
"Row({0}, TableIndex.{1}, EditAndContinueOperation.{2})",
MetadataTokens.GetRowNumber(row.Handle),
index,
row.Operation)
End Function
Private Shared Function EncMapRowToString(handle As Handle) As String
Dim index As TableIndex = 0
MetadataTokens.TryGetTableIndex(handle.HandleType, index)
Return String.Format(
"Handle({0}, TableIndex.{1})",
MetadataTokens.GetRowNumber(handle),
index)
End Function
#End Region
End Class
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册