diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index 0eb79d4b7c8726a293ff1c7f0b036b18f57ac302..715769af8d7db9be078796967b8c420ff3241c26 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using Microsoft.CodeAnalysis.CodeGen; @@ -64,6 +65,40 @@ public void DeltaHeapsStartWithEmptyItem() } } + [Fact] + public void Delta_AssemblyDefTable() + { + var source0 = @"public class C { public static void F() { System.Console.WriteLine(1); } }"; + var source1 = @"public class C { public static void F() { System.Console.WriteLine(2); } }"; + + var compilation0 = CreateCompilationWithMscorlib45(source0, options: ComSafeDebugDll); + var compilation1 = compilation0.WithSource(source1); + + var f0 = compilation0.GetMember("C.F"); + var f1 = compilation1.GetMember("C.F"); + + var v0 = CompileAndVerify(compilation0); + var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo); + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, f0, f1, preserveLocalVariables: true))); + + var reader1 = diff1.GetMetadata().Reader; + + var assemblyDef = reader1.GetAssemblyDefinition(); + Assert.False(assemblyDef.Name.IsNil); + Assert.Equal(0, assemblyDef.Version.Major); + Assert.Equal(0, assemblyDef.Version.Minor); + Assert.Equal(0, assemblyDef.Version.Revision); + Assert.Equal(0, assemblyDef.Version.Build); + Assert.True(assemblyDef.PublicKey.IsNil); + Assert.True(assemblyDef.Culture.IsNil); + Assert.Equal((AssemblyFlags)0, assemblyDef.Flags); + Assert.Equal(AssemblyHashAlgorithm.Sha1, assemblyDef.HashAlgorithm); + } + [Fact] public void ModifyMethod() { diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataHeapsBuilder.cs b/src/Compilers/Core/Portable/PEWriter/MetadataHeapsBuilder.cs index 931e321e7106772288a259b431b13751469948fe..42f371b9c7edca21039f44535b56f8e055cafd28 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataHeapsBuilder.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataHeapsBuilder.cs @@ -21,9 +21,9 @@ internal struct StringIdx : IEquatable // index in _stringIndexToHeapPositionMap public readonly int MapIndex; - internal StringIdx(int virtIdx) + internal StringIdx(int mapIndex) { - MapIndex = virtIdx; + MapIndex = mapIndex; } public bool Equals(StringIdx other) @@ -103,7 +103,7 @@ internal sealed class MetadataHeapsBuilder // #String heap private Dictionary _strings = new Dictionary(128); - private int[] _stringIndexToHeapPositionMap; + private int[] _stringIndexToResolvedOffsetMap; private BlobBuilder _stringWriter; private readonly int _stringHeapStartOffset; @@ -124,10 +124,11 @@ internal sealed class MetadataHeapsBuilder int blobHeapStartOffset = 0, int guidHeapStartOffset = 0) { - // Add zero-th entry to heaps. - // Full metadata represent empty blob/string at heap index 0. - // Delta metadata requires these to avoid nil generation-relative handles, - // which are technically viable but confusing. + // Add zero-th entry to all heaps, even in EnC delta. + // We don't want generation-relative handles to ever be IsNil. + // In both full and delta metadata all nil heap handles should have zero value. + // There should be no blob handle that references the 0 byte added at the + // beginning of the delta blob. _userStringWriter.WriteByte(0); _blobs.Add(ImmutableArray.Empty, new BlobIdx(0)); @@ -254,12 +255,12 @@ public StringIdx GetStringIndex(string str) public int ResolveStringIndex(StringIdx index) { - return _stringHeapStartOffset + _stringIndexToHeapPositionMap[index.MapIndex]; + return _stringIndexToResolvedOffsetMap[index.MapIndex]; } public int ResolveBlobIndex(BlobIdx index) { - return _blobHeapStartOffset + index.HeapPosition; + return (index.HeapPosition == 0) ? 0 : _blobHeapStartOffset + index.HeapPosition; } public int GetUserStringToken(string str) @@ -364,26 +365,26 @@ private void SerializeStringHeap() _stringWriter = new BlobBuilder(1024); // Create VirtIdx to Idx map and add entry for empty string - _stringIndexToHeapPositionMap = new int[sorted.Count + 1]; + _stringIndexToResolvedOffsetMap = new int[sorted.Count + 1]; - _stringIndexToHeapPositionMap[0] = 0; + _stringIndexToResolvedOffsetMap[0] = 0; _stringWriter.WriteByte(0); // Find strings that can be folded string prev = string.Empty; foreach (KeyValuePair entry in sorted) { - int position = _stringWriter.Position; - + int position = _stringHeapStartOffset + _stringWriter.Position; + // It is important to use ordinal comparison otherwise we'll use the current culture! if (prev.EndsWith(entry.Key, StringComparison.Ordinal)) { // Map over the tail of prev string. Watch for null-terminator of prev string. - _stringIndexToHeapPositionMap[entry.Value.MapIndex] = position - (s_utf8Encoding.GetByteCount(entry.Key) + 1); + _stringIndexToResolvedOffsetMap[entry.Value.MapIndex] = position - (s_utf8Encoding.GetByteCount(entry.Key) + 1); } else { - _stringIndexToHeapPositionMap[entry.Value.MapIndex] = position; + _stringIndexToResolvedOffsetMap[entry.Value.MapIndex] = position; _stringWriter.WriteString(entry.Key, s_utf8Encoding); _stringWriter.WriteByte(0); }