Refactor metadata serializers (dotnet/corefx#9588)
authorTomáš Matoušek <tmat@users.noreply.github.com>
Fri, 24 Jun 2016 22:50:58 +0000 (15:50 -0700)
committerGitHub <noreply@github.com>
Fri, 24 Jun 2016 22:50:58 +0000 (15:50 -0700)
* Refactor metadata serializers

* Split file

* Renames

* More API testing and polish

Commit migrated from https://github.com/dotnet/corefx/commit/d03addae82910375f093dc896ce53528ae2ca443

28 files changed:
src/libraries/System.Reflection.Metadata/src/Resources/Strings.resx
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataAggregator.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataBuilder.Heaps.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataBuilder.Tables.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataBuilder.cs [new file with mode: 0644]
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataReaderExtensions.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataRootBuilder.cs [new file with mode: 0644]
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSerializer.cs [deleted file]
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSizes.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataTokens.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/PortablePdbBuilder.cs [new file with mode: 0644]
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/SerializedMetadataHeaps.cs [new file with mode: 0644]
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/TableIndex.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/HeapSizeFlag.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/MetadataFlags.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReader.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedPEBuilder.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/Throw.cs
src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/MetadataBuilderTests.cs
src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/MetadataRootBuilderTests.cs [new file with mode: 0644]
src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/MetadataTokensTests.cs
src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/PortablePdbBuilderTests.cs [new file with mode: 0644]
src/libraries/System.Reflection.Metadata/tests/Metadata/LargeTablesAndHeapsTests.cs
src/libraries/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs
src/libraries/System.Reflection.Metadata/tests/System.Reflection.Metadata.Tests.csproj
src/libraries/System.Reflection.Metadata/tests/TestUtilities/AssertEx.cs
src/libraries/System.Reflection.Metadata/tests/TestUtilities/ByteArrayUtilities.cs [new file with mode: 0644]

index 9f07c03..2f18096 100644 (file)
   <data name="ExpectedListOfSize" xml:space="preserve">
     <value>Expected list of size {0}.</value>
   </data>
+  <data name="ExpectedArrayOfSize" xml:space="preserve">
+    <value>Expected array of size {0}.</value>
+  </data>
   <data name="ExpectedNonEmptyList" xml:space="preserve">
     <value>Expected non-empty list.</value>
   </data>
   <data name="MustNotReturnNull" xml:space="preserve">
     <value>{0} must not return null.</value>
   </data>
+  <data name="MetadataVersionTooLong" xml:space="preserve">
+    <value>Metadata version too long.</value>
+  </data>
+  <data name="RowCountMustBeZero" xml:space="preserve">
+    <value>Row count must be zero for table #{0}.</value>
+  </data>
+  <data name="RowCountOutOfRange" xml:space="preserve">
+    <value>Row count specified for table index {0} is out of allowed range.</value>
+  </data>
 </root>
index 9ebcf82..b4e4aac 100644 (file)
@@ -41,6 +41,9 @@
     <Compile Include="System\Reflection\Metadata\Ecma335\Encoding\FunctionPointerAttributes.cs" />
     <Compile Include="System\Reflection\Metadata\Ecma335\Encoding\MethodBodiesEncoder.cs" />
     <Compile Include="System\Reflection\Metadata\Ecma335\Encoding\MethodBodyAttributes.cs" />
+    <Compile Include="System\Reflection\Metadata\Ecma335\MetadataBuilder.cs" />
+    <Compile Include="System\Reflection\Metadata\Ecma335\MetadataRootBuilder.cs" />
+    <Compile Include="System\Reflection\Metadata\Ecma335\SerializedMetadataHeaps.cs" />
     <Compile Include="System\Reflection\Metadata\EntityHandle.cs" />
     <Compile Include="System\Reflection\Metadata\PooledBlobBuilder.cs" />
     <Compile Include="System\Reflection\Metadata\Blob.cs" />
@@ -62,7 +65,7 @@
     <Compile Include="System\Reflection\Metadata\Ecma335\Encoding\LabelHandle.cs" />
     <Compile Include="System\Reflection\Metadata\IL\ILOpCode.cs" />
     <Compile Include="System\Reflection\Metadata\Ecma335\CodedIndex.cs" />
-    <Compile Include="System\Reflection\Metadata\Ecma335\MetadataSerializer.cs" />
+    <Compile Include="System\Reflection\Metadata\Ecma335\PortablePdbBuilder.cs" />
     <Compile Include="System\Reflection\Metadata\Ecma335\MetadataBuilder.Tables.cs" />
     <Compile Include="System\Reflection\Metadata\Ecma335\Encoding\BlobEncoders.cs" />
     <Compile Include="System\Reflection\Metadata\Ecma335\Encoding\MethodBodyEncoder.cs" />
index 4cac995..0ae7a0b 100644 (file)
@@ -193,7 +193,7 @@ namespace System.Reflection.Metadata.Ecma335
         // internal for testing
         internal static RowCounts[][] GetBaseRowCounts(IReadOnlyList<int> baseRowCounts, int generations)
         {
-            var rowCounts = new RowCounts[TableIndexExtensions.Count][];
+            var rowCounts = new RowCounts[MetadataTokens.TableCount][];
 
             for (int t = 0; t < rowCounts.Length; t++)
             {
index afb1685..829a72a 100644 (file)
@@ -41,12 +41,8 @@ namespace System.Reflection.Metadata.Ecma335
 
         // #String heap
         private Dictionary<string, StringHandle> _strings = new Dictionary<string, StringHandle>(256);
-        private readonly HeapBlobBuilder _stringBuilder = new HeapBlobBuilder(4 * 1024);
         private readonly int _stringHeapStartOffset;
-
-        // map allocated when the String heap is serialized:
-        private int[] _stringVirtualIndexToHeapOffsetMap;
-        private bool HeapsCompleted => _stringVirtualIndexToHeapOffsetMap != null;
+        private int _stringHeapCapacity = 4 * 1024;
 
         // #Blob heap
         private readonly Dictionary<ImmutableArray<byte>, BlobHandle> _blobs = new Dictionary<ImmutableArray<byte>, BlobHandle>(1024, ByteSequenceComparer.Instance);
@@ -166,7 +162,7 @@ namespace System.Reflection.Metadata.Ecma335
                     break;
 
                 case HeapIndex.String:
-                    _stringBuilder.SetCapacity(byteCount);
+                    _stringHeapCapacity = byteCount;
                     break;
 
                 case HeapIndex.UserString:
@@ -180,7 +176,7 @@ namespace System.Reflection.Metadata.Ecma335
         }
 
         // internal for testing
-        internal int SerializeHandle(StringHandle handle) => _stringVirtualIndexToHeapOffsetMap[handle.GetWriterVirtualIndex()];
+        internal int SerializeHandle(ImmutableArray<int> map, StringHandle handle) => map[handle.GetWriterVirtualIndex()];
         internal int SerializeHandle(BlobHandle handle) => handle.GetHeapOffset();
         internal int SerializeHandle(GuidHandle handle) => handle.Index;
         internal int SerializeHandle(UserStringHandle handle) => handle.GetHeapOffset();
@@ -235,8 +231,6 @@ namespace System.Reflection.Metadata.Ecma335
             BlobHandle handle;
             if (!_blobs.TryGetValue(value, out handle))
             {
-                Debug.Assert(!HeapsCompleted);
-
                 handle = BlobHandle.FromOffset(_blobHeapStartOffset + _blobHeapSize);
                 _blobs.Add(value, handle);
 
@@ -341,8 +335,6 @@ namespace System.Reflection.Metadata.Ecma335
 
         private GuidHandle GetNewGuidHandle()
         {
-            Debug.Assert(!HeapsCompleted);
-
             // Unlike #Blob, #String and #US streams delta #GUID stream is padded to the 
             // size of the previous generation #GUID stream before new GUIDs are added.
             // The first GUID added in a delta will thus have an index that equals the number 
@@ -374,7 +366,6 @@ namespace System.Reflection.Metadata.Ecma335
             }
             else if (!_strings.TryGetValue(value, out handle))
             {
-                Debug.Assert(!HeapsCompleted);
                 handle = StringHandle.FromWriterVirtualIndex(_strings.Count + 1); // idx 0 is reserved for empty string
                 _strings.Add(value, handle);
             }
@@ -429,7 +420,6 @@ namespace System.Reflection.Metadata.Ecma335
             UserStringHandle handle;
             if (!_userStrings.TryGetValue(value, out handle))
             {
-                Debug.Assert(!HeapsCompleted);
                 handle = GetNewUserStringHandle();
 
                 _userStrings.Add(value, handle);
@@ -453,62 +443,50 @@ namespace System.Reflection.Metadata.Ecma335
             return UserStringHandle.FromOffset(offset);
         }
 
-        internal void CompleteHeaps()
-        {
-            Debug.Assert(!HeapsCompleted);
-            SerializeStringHeap();
-        }
-
-        public ImmutableArray<int> GetHeapSizes()
-        {
-            var heapSizes = new int[MetadataTokens.HeapCount];
-
-            heapSizes[(int)HeapIndex.UserString] = _userStringBuilder.Count;
-            heapSizes[(int)HeapIndex.String] = _stringBuilder.Count;
-            heapSizes[(int)HeapIndex.Blob] = _blobHeapSize;
-            heapSizes[(int)HeapIndex.Guid] = _guidBuilder.Count;
-
-            return ImmutableArray.CreateRange(heapSizes);
-        }
-
         /// <summary>
         /// Fills in stringIndexMap with data from stringIndex and write to stringWriter.
         /// Releases stringIndex as the stringTable is sealed after this point.
         /// </summary>
-        private void SerializeStringHeap()
+        private static ImmutableArray<int> SerializeStringHeap(
+            BlobBuilder heapBuilder,
+            Dictionary<string, StringHandle> strings,
+            int stringHeapStartOffset)
         {
             // Sort by suffix and remove stringIndex
-            var sorted = new List<KeyValuePair<string, StringHandle>>(_strings);
-            sorted.Sort(new SuffixSort());
-            _strings = null;
+            var sorted = new List<KeyValuePair<string, StringHandle>>(strings);
+            sorted.Sort(SuffixSort.Instance);
 
             // Create VirtIdx to Idx map and add entry for empty string
-            _stringVirtualIndexToHeapOffsetMap = new int[sorted.Count + 1];
+            int totalCount = sorted.Count + 1;
+            var stringVirtualIndexToHeapOffsetMap = ImmutableArray.CreateBuilder<int>(totalCount);
+            stringVirtualIndexToHeapOffsetMap.Count = totalCount;
 
-            _stringVirtualIndexToHeapOffsetMap[0] = 0;
-            _stringBuilder.WriteByte(0);
+            stringVirtualIndexToHeapOffsetMap[0] = 0;
+            heapBuilder.WriteByte(0);
 
             // Find strings that can be folded
             string prev = string.Empty;
             foreach (KeyValuePair<string, StringHandle> entry in sorted)
             {
-                int position = _stringHeapStartOffset + _stringBuilder.Count;
+                int position = stringHeapStartOffset + heapBuilder.Count;
                 
                 // It is important to use ordinal comparison otherwise we'll use the current culture!
                 if (prev.EndsWith(entry.Key, StringComparison.Ordinal) && !BlobUtilities.IsLowSurrogateChar(entry.Key[0]))
                 {
                     // Map over the tail of prev string. Watch for null-terminator of prev string.
-                    _stringVirtualIndexToHeapOffsetMap[entry.Value.GetWriterVirtualIndex()] = position - (BlobUtilities.GetUTF8ByteCount(entry.Key) + 1);
+                    stringVirtualIndexToHeapOffsetMap[entry.Value.GetWriterVirtualIndex()] = position - (BlobUtilities.GetUTF8ByteCount(entry.Key) + 1);
                 }
                 else
                 {
-                    _stringVirtualIndexToHeapOffsetMap[entry.Value.GetWriterVirtualIndex()] = position;
-                    _stringBuilder.WriteUTF8(entry.Key, allowUnpairedSurrogates: false);
-                    _stringBuilder.WriteByte(0);
+                    stringVirtualIndexToHeapOffsetMap[entry.Value.GetWriterVirtualIndex()] = position;
+                    heapBuilder.WriteUTF8(entry.Key, allowUnpairedSurrogates: false);
+                    heapBuilder.WriteByte(0);
                 }
 
                 prev = entry.Key;
             }
+
+            return stringVirtualIndexToHeapOffsetMap.MoveToImmutable();
         }
 
         /// <summary>
@@ -517,6 +495,8 @@ namespace System.Reflection.Metadata.Ecma335
         /// </summary>
         private sealed class SuffixSort : IComparer<KeyValuePair<string, StringHandle>>
         {
+            internal static SuffixSort Instance = new SuffixSort();
+
             public int Compare(KeyValuePair<string, StringHandle> xPair, KeyValuePair<string, StringHandle> yPair)
             {
                 string x = xPair.Key;
@@ -539,11 +519,9 @@ namespace System.Reflection.Metadata.Ecma335
             }
         }
 
-        internal void WriteHeapsTo(BlobBuilder builder)
+        internal void WriteHeapsTo(BlobBuilder builder, BlobBuilder stringHeap)
         {
-            Debug.Assert(HeapsCompleted);
-
-            WriteAligned(_stringBuilder, builder);
+            WriteAligned(stringHeap, builder);
             WriteAligned(_userStringBuilder, builder);
             WriteAligned(_guidBuilder, builder);
             WriteAlignedBlobHeap(builder);
index 563a107..65171a7 100644 (file)
@@ -216,6 +216,137 @@ namespace System.Reflection.Metadata.Ecma335
             }
         }
 
+        /// <summary>
+        /// Returns the current number of entires in the specified table.
+        /// </summary>
+        /// <param name="table">Table index.</param>
+        /// <returns>The number of entires in the table.</returns>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="table"/> is not a valid table index.</exception>
+        public int GetRowCount(TableIndex table)
+        {
+            switch (table)
+            {
+                case TableIndex.Assembly                : return _assemblyRow.HasValue ? 1 : 0;
+                case TableIndex.AssemblyRef             : return _assemblyRefTable.Count;
+                case TableIndex.ClassLayout             : return _classLayoutTable.Count;
+                case TableIndex.Constant                : return _constantTable.Count;
+                case TableIndex.CustomAttribute         : return _customAttributeTable.Count;
+                case TableIndex.DeclSecurity            : return _declSecurityTable.Count;
+                case TableIndex.EncLog                  : return _encLogTable.Count;
+                case TableIndex.EncMap                  : return _encMapTable.Count;
+                case TableIndex.EventMap                : return _eventMapTable.Count;
+                case TableIndex.Event                   : return _eventTable.Count;
+                case TableIndex.ExportedType            : return _exportedTypeTable.Count;
+                case TableIndex.FieldLayout             : return _fieldLayoutTable.Count;
+                case TableIndex.FieldMarshal            : return _fieldMarshalTable.Count;
+                case TableIndex.FieldRva                : return _fieldRvaTable.Count;
+                case TableIndex.Field                   : return _fieldTable.Count;
+                case TableIndex.File                    : return _fileTable.Count;
+                case TableIndex.GenericParamConstraint  : return _genericParamConstraintTable.Count;
+                case TableIndex.GenericParam            : return _genericParamTable.Count;
+                case TableIndex.ImplMap                 : return _implMapTable.Count;
+                case TableIndex.InterfaceImpl           : return _interfaceImplTable.Count;
+                case TableIndex.ManifestResource        : return _manifestResourceTable.Count;
+                case TableIndex.MemberRef               : return _memberRefTable.Count;
+                case TableIndex.MethodImpl              : return _methodImplTable.Count;
+                case TableIndex.MethodSemantics         : return _methodSemanticsTable.Count;
+                case TableIndex.MethodSpec              : return _methodSpecTable.Count;
+                case TableIndex.MethodDef               : return _methodDefTable.Count;
+                case TableIndex.ModuleRef               : return _moduleRefTable.Count;
+                case TableIndex.Module                  : return _moduleRow.HasValue ? 1 : 0;
+                case TableIndex.NestedClass             : return _nestedClassTable.Count;
+                case TableIndex.Param                   : return _paramTable.Count;
+                case TableIndex.PropertyMap             : return _propertyMapTable.Count;
+                case TableIndex.Property                : return _propertyTable.Count;
+                case TableIndex.StandAloneSig           : return _standAloneSigTable.Count;
+                case TableIndex.TypeDef                 : return _typeDefTable.Count;
+                case TableIndex.TypeRef                 : return _typeRefTable.Count;
+                case TableIndex.TypeSpec                : return _typeSpecTable.Count;
+                case TableIndex.Document                : return _documentTable.Count;
+                case TableIndex.MethodDebugInformation  : return _methodDebugInformationTable.Count;
+                case TableIndex.LocalScope              : return _localScopeTable.Count;
+                case TableIndex.LocalVariable           : return _localVariableTable.Count;
+                case TableIndex.LocalConstant           : return _localConstantTable.Count;
+                case TableIndex.StateMachineMethod      : return _stateMachineMethodTable.Count;
+                case TableIndex.ImportScope             : return _importScopeTable.Count;
+                case TableIndex.CustomDebugInformation  : return _customDebugInformationTable.Count;
+
+                case TableIndex.AssemblyOS:
+                case TableIndex.AssemblyProcessor:
+                case TableIndex.AssemblyRefOS:
+                case TableIndex.AssemblyRefProcessor:
+                case TableIndex.EventPtr:
+                case TableIndex.FieldPtr:
+                case TableIndex.MethodPtr:
+                case TableIndex.ParamPtr:
+                case TableIndex.PropertyPtr:
+                    return 0;
+
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(table));
+            }
+        }
+
+        /// <summary>
+        /// Returns the current number of entires in each table.
+        /// </summary>
+        /// <returns>
+        /// An array of size <see cref="MetadataTokens.TableCount"/> with each item filled with the current row count of the corresponding table.
+        /// </returns>
+        public ImmutableArray<int> GetRowCounts()
+        {
+            var rowCounts = ImmutableArray.CreateBuilder<int>(MetadataTokens.TableCount);
+            rowCounts.Count = MetadataTokens.TableCount;
+
+            rowCounts[(int)TableIndex.Assembly] = _assemblyRow.HasValue ? 1 : 0;
+            rowCounts[(int)TableIndex.AssemblyRef] = _assemblyRefTable.Count;
+            rowCounts[(int)TableIndex.ClassLayout] = _classLayoutTable.Count;
+            rowCounts[(int)TableIndex.Constant] = _constantTable.Count;
+            rowCounts[(int)TableIndex.CustomAttribute] = _customAttributeTable.Count;
+            rowCounts[(int)TableIndex.DeclSecurity] = _declSecurityTable.Count;
+            rowCounts[(int)TableIndex.EncLog] = _encLogTable.Count;
+            rowCounts[(int)TableIndex.EncMap] = _encMapTable.Count;
+            rowCounts[(int)TableIndex.EventMap] = _eventMapTable.Count;
+            rowCounts[(int)TableIndex.Event] = _eventTable.Count;
+            rowCounts[(int)TableIndex.ExportedType] = _exportedTypeTable.Count;
+            rowCounts[(int)TableIndex.FieldLayout] = _fieldLayoutTable.Count;
+            rowCounts[(int)TableIndex.FieldMarshal] = _fieldMarshalTable.Count;
+            rowCounts[(int)TableIndex.FieldRva] = _fieldRvaTable.Count;
+            rowCounts[(int)TableIndex.Field] = _fieldTable.Count;
+            rowCounts[(int)TableIndex.File] = _fileTable.Count;
+            rowCounts[(int)TableIndex.GenericParamConstraint] = _genericParamConstraintTable.Count;
+            rowCounts[(int)TableIndex.GenericParam] = _genericParamTable.Count;
+            rowCounts[(int)TableIndex.ImplMap] = _implMapTable.Count;
+            rowCounts[(int)TableIndex.InterfaceImpl] = _interfaceImplTable.Count;
+            rowCounts[(int)TableIndex.ManifestResource] = _manifestResourceTable.Count;
+            rowCounts[(int)TableIndex.MemberRef] = _memberRefTable.Count;
+            rowCounts[(int)TableIndex.MethodImpl] = _methodImplTable.Count;
+            rowCounts[(int)TableIndex.MethodSemantics] = _methodSemanticsTable.Count;
+            rowCounts[(int)TableIndex.MethodSpec] = _methodSpecTable.Count;
+            rowCounts[(int)TableIndex.MethodDef] = _methodDefTable.Count;
+            rowCounts[(int)TableIndex.ModuleRef] = _moduleRefTable.Count;
+            rowCounts[(int)TableIndex.Module] = _moduleRow.HasValue ? 1 : 0;
+            rowCounts[(int)TableIndex.NestedClass] = _nestedClassTable.Count;
+            rowCounts[(int)TableIndex.Param] = _paramTable.Count;
+            rowCounts[(int)TableIndex.PropertyMap] = _propertyMapTable.Count;
+            rowCounts[(int)TableIndex.Property] = _propertyTable.Count;
+            rowCounts[(int)TableIndex.StandAloneSig] = _standAloneSigTable.Count;
+            rowCounts[(int)TableIndex.TypeDef] = _typeDefTable.Count;
+            rowCounts[(int)TableIndex.TypeRef] = _typeRefTable.Count;
+            rowCounts[(int)TableIndex.TypeSpec] = _typeSpecTable.Count;
+
+            rowCounts[(int)TableIndex.Document] = _documentTable.Count;
+            rowCounts[(int)TableIndex.MethodDebugInformation] = _methodDebugInformationTable.Count;
+            rowCounts[(int)TableIndex.LocalScope] = _localScopeTable.Count;
+            rowCounts[(int)TableIndex.LocalVariable] = _localVariableTable.Count;
+            rowCounts[(int)TableIndex.LocalConstant] = _localConstantTable.Count;
+            rowCounts[(int)TableIndex.StateMachineMethod] = _stateMachineMethodTable.Count;
+            rowCounts[(int)TableIndex.ImportScope] = _importScopeTable.Count;
+            rowCounts[(int)TableIndex.CustomDebugInformation] = _customDebugInformationTable.Count;
+
+            return rowCounts.MoveToImmutable();
+        }
+
         #region Building
 
         // Note on argument value checking:
@@ -1205,290 +1336,238 @@ namespace System.Reflection.Metadata.Ecma335
 
         #endregion
 
-        public ImmutableArray<int> GetRowCounts()
-        {
-            var rowCounts = new int[MetadataTokens.TableCount];
-
-            rowCounts[(int)TableIndex.Assembly] = _assemblyRow.HasValue ? 1 : 0;
-            rowCounts[(int)TableIndex.AssemblyRef] = _assemblyRefTable.Count;
-            rowCounts[(int)TableIndex.ClassLayout] = _classLayoutTable.Count;
-            rowCounts[(int)TableIndex.Constant] = _constantTable.Count;
-            rowCounts[(int)TableIndex.CustomAttribute] = _customAttributeTable.Count;
-            rowCounts[(int)TableIndex.DeclSecurity] = _declSecurityTable.Count;
-            rowCounts[(int)TableIndex.EncLog] = _encLogTable.Count;
-            rowCounts[(int)TableIndex.EncMap] = _encMapTable.Count;
-            rowCounts[(int)TableIndex.EventMap] = _eventMapTable.Count;
-            rowCounts[(int)TableIndex.Event] = _eventTable.Count;
-            rowCounts[(int)TableIndex.ExportedType] = _exportedTypeTable.Count;
-            rowCounts[(int)TableIndex.FieldLayout] = _fieldLayoutTable.Count;
-            rowCounts[(int)TableIndex.FieldMarshal] = _fieldMarshalTable.Count;
-            rowCounts[(int)TableIndex.FieldRva] = _fieldRvaTable.Count;
-            rowCounts[(int)TableIndex.Field] = _fieldTable.Count;
-            rowCounts[(int)TableIndex.File] = _fileTable.Count;
-            rowCounts[(int)TableIndex.GenericParamConstraint] = _genericParamConstraintTable.Count;
-            rowCounts[(int)TableIndex.GenericParam] = _genericParamTable.Count;
-            rowCounts[(int)TableIndex.ImplMap] = _implMapTable.Count;
-            rowCounts[(int)TableIndex.InterfaceImpl] = _interfaceImplTable.Count;
-            rowCounts[(int)TableIndex.ManifestResource] = _manifestResourceTable.Count;
-            rowCounts[(int)TableIndex.MemberRef] = _memberRefTable.Count;
-            rowCounts[(int)TableIndex.MethodImpl] = _methodImplTable.Count;
-            rowCounts[(int)TableIndex.MethodSemantics] = _methodSemanticsTable.Count;
-            rowCounts[(int)TableIndex.MethodSpec] = _methodSpecTable.Count;
-            rowCounts[(int)TableIndex.MethodDef] = _methodDefTable.Count;
-            rowCounts[(int)TableIndex.ModuleRef] = _moduleRefTable.Count;
-            rowCounts[(int)TableIndex.Module] = _moduleRow.HasValue ? 1 : 0;
-            rowCounts[(int)TableIndex.NestedClass] = _nestedClassTable.Count;
-            rowCounts[(int)TableIndex.Param] = _paramTable.Count;
-            rowCounts[(int)TableIndex.PropertyMap] = _propertyMapTable.Count;
-            rowCounts[(int)TableIndex.Property] = _propertyTable.Count;
-            rowCounts[(int)TableIndex.StandAloneSig] = _standAloneSigTable.Count;
-            rowCounts[(int)TableIndex.TypeDef] = _typeDefTable.Count;
-            rowCounts[(int)TableIndex.TypeRef] = _typeRefTable.Count;
-            rowCounts[(int)TableIndex.TypeSpec] = _typeSpecTable.Count;
-
-            rowCounts[(int)TableIndex.Document] = _documentTable.Count;
-            rowCounts[(int)TableIndex.MethodDebugInformation] = _methodDebugInformationTable.Count;
-            rowCounts[(int)TableIndex.LocalScope] = _localScopeTable.Count;
-            rowCounts[(int)TableIndex.LocalVariable] = _localVariableTable.Count;
-            rowCounts[(int)TableIndex.LocalConstant] = _localConstantTable.Count;
-            rowCounts[(int)TableIndex.StateMachineMethod] = _stateMachineMethodTable.Count;
-            rowCounts[(int)TableIndex.ImportScope] = _importScopeTable.Count;
-            rowCounts[(int)TableIndex.CustomDebugInformation] = _customDebugInformationTable.Count;
-
-            return ImmutableArray.CreateRange(rowCounts);
-        }
-
         #region Serialization
 
         internal void SerializeMetadataTables(
             BlobBuilder writer,
             MetadataSizes metadataSizes,
+            ImmutableArray<int> stringMap,
             int methodBodyStreamRva,
             int mappedFieldDataStreamRva)
         {
             int startPosition = writer.Count;
 
-            this.SerializeTablesHeader(writer, metadataSizes);
+            SerializeTablesHeader(writer, metadataSizes);
 
             if (metadataSizes.IsPresent(TableIndex.Module))
             {
-                SerializeModuleTable(writer, metadataSizes);
+                SerializeModuleTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.TypeRef))
             {
-                this.SerializeTypeRefTable(writer, metadataSizes);
+                SerializeTypeRefTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.TypeDef))
             {
-                this.SerializeTypeDefTable(writer, metadataSizes);
+                SerializeTypeDefTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.Field))
             {
-                this.SerializeFieldTable(writer, metadataSizes);
+                SerializeFieldTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.MethodDef))
             {
-                this.SerializeMethodDefTable(writer, metadataSizes, methodBodyStreamRva);
+                SerializeMethodDefTable(writer, stringMap, metadataSizes, methodBodyStreamRva);
             }
 
             if (metadataSizes.IsPresent(TableIndex.Param))
             {
-                this.SerializeParamTable(writer, metadataSizes);
+                SerializeParamTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.InterfaceImpl))
             {
-                this.SerializeInterfaceImplTable(writer, metadataSizes);
+                SerializeInterfaceImplTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.MemberRef))
             {
-                this.SerializeMemberRefTable(writer, metadataSizes);
+                SerializeMemberRefTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.Constant))
             {
-                this.SerializeConstantTable(writer, metadataSizes);
+                SerializeConstantTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.CustomAttribute))
             {
-                this.SerializeCustomAttributeTable(writer, metadataSizes);
+                SerializeCustomAttributeTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.FieldMarshal))
             {
-                this.SerializeFieldMarshalTable(writer, metadataSizes);
+                SerializeFieldMarshalTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.DeclSecurity))
             {
-                this.SerializeDeclSecurityTable(writer, metadataSizes);
+                SerializeDeclSecurityTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.ClassLayout))
             {
-                this.SerializeClassLayoutTable(writer, metadataSizes);
+                SerializeClassLayoutTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.FieldLayout))
             {
-                this.SerializeFieldLayoutTable(writer, metadataSizes);
+                SerializeFieldLayoutTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.StandAloneSig))
             {
-                this.SerializeStandAloneSigTable(writer, metadataSizes);
+                SerializeStandAloneSigTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.EventMap))
             {
-                this.SerializeEventMapTable(writer, metadataSizes);
+                SerializeEventMapTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.Event))
             {
-                this.SerializeEventTable(writer, metadataSizes);
+                SerializeEventTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.PropertyMap))
             {
-                this.SerializePropertyMapTable(writer, metadataSizes);
+                SerializePropertyMapTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.Property))
             {
-                this.SerializePropertyTable(writer, metadataSizes);
+                SerializePropertyTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.MethodSemantics))
             {
-                this.SerializeMethodSemanticsTable(writer, metadataSizes);
+                SerializeMethodSemanticsTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.MethodImpl))
             {
-                this.SerializeMethodImplTable(writer, metadataSizes);
+                SerializeMethodImplTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.ModuleRef))
             {
-                this.SerializeModuleRefTable(writer, metadataSizes);
+                SerializeModuleRefTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.TypeSpec))
             {
-                this.SerializeTypeSpecTable(writer, metadataSizes);
+                SerializeTypeSpecTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.ImplMap))
             {
-                this.SerializeImplMapTable(writer, metadataSizes);
+                SerializeImplMapTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.FieldRva))
             {
-                this.SerializeFieldRvaTable(writer, metadataSizes, mappedFieldDataStreamRva);
+                SerializeFieldRvaTable(writer, metadataSizes, mappedFieldDataStreamRva);
             }
 
             if (metadataSizes.IsPresent(TableIndex.EncLog))
             {
-                this.SerializeEncLogTable(writer);
+                SerializeEncLogTable(writer);
             }
 
             if (metadataSizes.IsPresent(TableIndex.EncMap))
             {
-                this.SerializeEncMapTable(writer);
+                SerializeEncMapTable(writer);
             }
 
             if (metadataSizes.IsPresent(TableIndex.Assembly))
             {
-                this.SerializeAssemblyTable(writer, metadataSizes);
+                SerializeAssemblyTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.AssemblyRef))
             {
-                this.SerializeAssemblyRefTable(writer, metadataSizes);
+                SerializeAssemblyRefTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.File))
             {
-                this.SerializeFileTable(writer, metadataSizes);
+                SerializeFileTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.ExportedType))
             {
-                this.SerializeExportedTypeTable(writer, metadataSizes);
+                SerializeExportedTypeTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.ManifestResource))
             {
-                this.SerializeManifestResourceTable(writer, metadataSizes);
+                SerializeManifestResourceTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.NestedClass))
             {
-                this.SerializeNestedClassTable(writer, metadataSizes);
+                SerializeNestedClassTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.GenericParam))
             {
-                this.SerializeGenericParamTable(writer, metadataSizes);
+                SerializeGenericParamTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.MethodSpec))
             {
-                this.SerializeMethodSpecTable(writer, metadataSizes);
+                SerializeMethodSpecTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.GenericParamConstraint))
             {
-                this.SerializeGenericParamConstraintTable(writer, metadataSizes);
+                SerializeGenericParamConstraintTable(writer, metadataSizes);
             }
 
             // debug tables
             if (metadataSizes.IsPresent(TableIndex.Document))
             {
-                this.SerializeDocumentTable(writer, metadataSizes);
+                SerializeDocumentTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.MethodDebugInformation))
             {
-                this.SerializeMethodDebugInformationTable(writer, metadataSizes);
+                SerializeMethodDebugInformationTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.LocalScope))
             {
-                this.SerializeLocalScopeTable(writer, metadataSizes);
+                SerializeLocalScopeTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.LocalVariable))
             {
-                this.SerializeLocalVariableTable(writer, metadataSizes);
+                SerializeLocalVariableTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.LocalConstant))
             {
-                this.SerializeLocalConstantTable(writer, metadataSizes);
+                SerializeLocalConstantTable(writer, stringMap, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.ImportScope))
             {
-                this.SerializeImportScopeTable(writer, metadataSizes);
+                SerializeImportScopeTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.StateMachineMethod))
             {
-                this.SerializeStateMachineMethodTable(writer, metadataSizes);
+                SerializeStateMachineMethodTable(writer, metadataSizes);
             }
 
             if (metadataSizes.IsPresent(TableIndex.CustomDebugInformation))
             {
-                this.SerializeCustomDebugInformationTable(writer, metadataSizes);
+                SerializeCustomDebugInformationTable(writer, metadataSizes);
             }
 
             writer.WriteByte(0);
@@ -1518,9 +1597,9 @@ namespace System.Reflection.Metadata.Ecma335
                 heapSizes |= HeapSizeFlag.BlobHeapLarge;
             }
 
-            if (metadataSizes.IsMinimalDelta)
+            if (metadataSizes.IsEncDelta)
             {
-                heapSizes |= (HeapSizeFlag.EnCDeltas | HeapSizeFlag.DeletedMarks);
+                heapSizes |= (HeapSizeFlag.EncDeltas | HeapSizeFlag.DeletedMarks);
             }
 
             ulong sortedDebugTables = metadataSizes.PresentTablesMask & MetadataSizes.SortedDebugTables;
@@ -1542,12 +1621,12 @@ namespace System.Reflection.Metadata.Ecma335
         }
 
         // internal for testing
-        internal void SerializeModuleTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        internal void SerializeModuleTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             if (_moduleRow.HasValue)
             {
                 writer.WriteUInt16(_moduleRow.Value.Generation);
-                writer.WriteReference(SerializeHandle(_moduleRow.Value.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, _moduleRow.Value.Name), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(SerializeHandle(_moduleRow.Value.ModuleVersionId), metadataSizes.GuidReferenceIsSmall);
                 writer.WriteReference(SerializeHandle(_moduleRow.Value.EncId), metadataSizes.GuidReferenceIsSmall);
                 writer.WriteReference(SerializeHandle(_moduleRow.Value.EncBaseId), metadataSizes.GuidReferenceIsSmall);
@@ -1571,40 +1650,40 @@ namespace System.Reflection.Metadata.Ecma335
             }
         }
 
-        private void SerializeTypeRefTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeTypeRefTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (TypeRefRow typeRef in _typeRefTable)
             {
                 writer.WriteReference(typeRef.ResolutionScope, metadataSizes.ResolutionScopeCodedIndexIsSmall);
-                writer.WriteReference(SerializeHandle(typeRef.Name), metadataSizes.StringReferenceIsSmall);
-                writer.WriteReference(SerializeHandle(typeRef.Namespace), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, typeRef.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, typeRef.Namespace), metadataSizes.StringReferenceIsSmall);
             }
         }
 
-        private void SerializeTypeDefTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeTypeDefTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (TypeDefRow typeDef in _typeDefTable)
             {
                 writer.WriteUInt32(typeDef.Flags);
-                writer.WriteReference(SerializeHandle(typeDef.Name), metadataSizes.StringReferenceIsSmall);
-                writer.WriteReference(SerializeHandle(typeDef.Namespace), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, typeDef.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, typeDef.Namespace), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(typeDef.Extends, metadataSizes.TypeDefOrRefCodedIndexIsSmall);
                 writer.WriteReference(typeDef.FieldList, metadataSizes.FieldDefReferenceIsSmall);
                 writer.WriteReference(typeDef.MethodList, metadataSizes.MethodDefReferenceIsSmall);
             }
         }
 
-        private void SerializeFieldTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeFieldTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (FieldDefRow fieldDef in _fieldTable)
             {
                 writer.WriteUInt16(fieldDef.Flags);
-                writer.WriteReference(SerializeHandle(fieldDef.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, fieldDef.Name), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(SerializeHandle(fieldDef.Signature), metadataSizes.BlobReferenceIsSmall);
             }
         }
 
-        private void SerializeMethodDefTable(BlobBuilder writer, MetadataSizes metadataSizes, int methodBodyStreamRva)
+        private void SerializeMethodDefTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes, int methodBodyStreamRva)
         {
             foreach (MethodRow method in _methodDefTable)
             {
@@ -1619,19 +1698,19 @@ namespace System.Reflection.Metadata.Ecma335
 
                 writer.WriteUInt16(method.ImplFlags);
                 writer.WriteUInt16(method.Flags);
-                writer.WriteReference(SerializeHandle(method.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, method.Name), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(SerializeHandle(method.Signature), metadataSizes.BlobReferenceIsSmall);
                 writer.WriteReference(method.ParamList, metadataSizes.ParameterReferenceIsSmall);
             }
         }
 
-        private void SerializeParamTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeParamTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (ParamRow param in _paramTable)
             {
                 writer.WriteUInt16(param.Flags);
                 writer.WriteUInt16(param.Sequence);
-                writer.WriteReference(SerializeHandle(param.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, param.Name), metadataSizes.StringReferenceIsSmall);
             }
         }
 
@@ -1646,12 +1725,12 @@ namespace System.Reflection.Metadata.Ecma335
             }
         }
 
-        private void SerializeMemberRefTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeMemberRefTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (MemberRefRow memberRef in _memberRefTable)
             {
                 writer.WriteReference(memberRef.Class, metadataSizes.MemberRefParentCodedIndexIsSmall);
-                writer.WriteReference(SerializeHandle(memberRef.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, memberRef.Name), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(SerializeHandle(memberRef.Signature), metadataSizes.BlobReferenceIsSmall);
             }
         }
@@ -1758,12 +1837,12 @@ namespace System.Reflection.Metadata.Ecma335
             }
         }
 
-        private void SerializeEventTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeEventTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (EventRow eventRow in _eventTable)
             {
                 writer.WriteUInt16(eventRow.EventFlags);
-                writer.WriteReference(SerializeHandle(eventRow.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, eventRow.Name), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(eventRow.EventType, metadataSizes.TypeDefOrRefCodedIndexIsSmall);
             }
         }
@@ -1777,12 +1856,12 @@ namespace System.Reflection.Metadata.Ecma335
             }
         }
 
-        private void SerializePropertyTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializePropertyTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (PropertyRow property in _propertyTable)
             {
                 writer.WriteUInt16(property.PropFlags);
-                writer.WriteReference(SerializeHandle(property.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, property.Name), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(SerializeHandle(property.Type), metadataSizes.BlobReferenceIsSmall);
             }
         }
@@ -1817,11 +1896,11 @@ namespace System.Reflection.Metadata.Ecma335
             }
         }
 
-        private void SerializeModuleRefTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeModuleRefTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (ModuleRefRow moduleRef in _moduleRefTable)
             {
-                writer.WriteReference(SerializeHandle(moduleRef.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, moduleRef.Name), metadataSizes.StringReferenceIsSmall);
             }
         }
 
@@ -1833,7 +1912,7 @@ namespace System.Reflection.Metadata.Ecma335
             }
         }
 
-        private void SerializeImplMapTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeImplMapTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
 #if DEBUG
             for (int i = 1; i < _implMapTable.Count; i++)
@@ -1845,7 +1924,7 @@ namespace System.Reflection.Metadata.Ecma335
             {
                 writer.WriteUInt16(implMap.MappingFlags);
                 writer.WriteReference(implMap.MemberForwarded, metadataSizes.MemberForwardedCodedIndexIsSmall);
-                writer.WriteReference(SerializeHandle(implMap.ImportName), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, implMap.ImportName), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(implMap.ImportScope, metadataSizes.ModuleRefReferenceIsSmall);
             }
         }
@@ -1865,7 +1944,7 @@ namespace System.Reflection.Metadata.Ecma335
             }
         }
 
-        private void SerializeAssemblyTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeAssemblyTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             if (_assemblyRow.HasValue)
             {
@@ -1877,12 +1956,12 @@ namespace System.Reflection.Metadata.Ecma335
                 writer.WriteUInt16((ushort)version.Revision);
                 writer.WriteUInt32(_assemblyRow.Value.Flags);
                 writer.WriteReference(SerializeHandle(_assemblyRow.Value.AssemblyKey), metadataSizes.BlobReferenceIsSmall);
-                writer.WriteReference(SerializeHandle(_assemblyRow.Value.AssemblyName), metadataSizes.StringReferenceIsSmall);
-                writer.WriteReference(SerializeHandle(_assemblyRow.Value.AssemblyCulture), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, _assemblyRow.Value.AssemblyName), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, _assemblyRow.Value.AssemblyCulture), metadataSizes.StringReferenceIsSmall);
             }
         }
 
-        private void SerializeAssemblyRefTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeAssemblyRefTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (AssemblyRefTableRow row in _assemblyRefTable)
             {
@@ -1892,41 +1971,41 @@ namespace System.Reflection.Metadata.Ecma335
                 writer.WriteUInt16((ushort)row.Version.Revision);
                 writer.WriteUInt32(row.Flags);
                 writer.WriteReference(SerializeHandle(row.PublicKeyToken), metadataSizes.BlobReferenceIsSmall);
-                writer.WriteReference(SerializeHandle(row.Name), metadataSizes.StringReferenceIsSmall);
-                writer.WriteReference(SerializeHandle(row.Culture), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, row.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, row.Culture), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(SerializeHandle(row.HashValue), metadataSizes.BlobReferenceIsSmall);
             }
         }
 
-        private void SerializeFileTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeFileTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (FileTableRow fileReference in _fileTable)
             {
                 writer.WriteUInt32(fileReference.Flags);
-                writer.WriteReference(SerializeHandle(fileReference.FileName), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, fileReference.FileName), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(SerializeHandle(fileReference.HashValue), metadataSizes.BlobReferenceIsSmall);
             }
         }
 
-        private void SerializeExportedTypeTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeExportedTypeTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (ExportedTypeRow exportedType in _exportedTypeTable)
             {
                 writer.WriteUInt32(exportedType.Flags);
                 writer.WriteInt32(exportedType.TypeDefId);
-                writer.WriteReference(SerializeHandle(exportedType.TypeName), metadataSizes.StringReferenceIsSmall);
-                writer.WriteReference(SerializeHandle(exportedType.TypeNamespace), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, exportedType.TypeName), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, exportedType.TypeNamespace), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(exportedType.Implementation, metadataSizes.ImplementationCodedIndexIsSmall);
             }
         }
 
-        private void SerializeManifestResourceTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeManifestResourceTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (ManifestResourceRow manifestResource in _manifestResourceTable)
             {
                 writer.WriteUInt32(manifestResource.Offset);
                 writer.WriteUInt32(manifestResource.Flags);
-                writer.WriteReference(SerializeHandle(manifestResource.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, manifestResource.Name), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(manifestResource.Implementation, metadataSizes.ImplementationCodedIndexIsSmall);
             }
         }
@@ -1946,7 +2025,7 @@ namespace System.Reflection.Metadata.Ecma335
             }
         }
 
-        private void SerializeGenericParamTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeGenericParamTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
 #if DEBUG
             for (int i = 1; i < _genericParamTable.Count; i++)
@@ -1961,7 +2040,7 @@ namespace System.Reflection.Metadata.Ecma335
                 writer.WriteUInt16(genericParam.Number);
                 writer.WriteUInt16(genericParam.Flags);
                 writer.WriteReference(genericParam.Owner, metadataSizes.TypeOrMethodDefCodedIndexIsSmall);
-                writer.WriteReference(SerializeHandle(genericParam.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, genericParam.Name), metadataSizes.StringReferenceIsSmall);
             }
         }
 
@@ -2037,21 +2116,21 @@ namespace System.Reflection.Metadata.Ecma335
             }
         }
 
-        private void SerializeLocalVariableTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeLocalVariableTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (var row in _localVariableTable)
             {
                 writer.WriteUInt16(row.Attributes);
                 writer.WriteUInt16(row.Index);
-                writer.WriteReference(SerializeHandle(row.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, row.Name), metadataSizes.StringReferenceIsSmall);
             }
         }
 
-        private void SerializeLocalConstantTable(BlobBuilder writer, MetadataSizes metadataSizes)
+        private void SerializeLocalConstantTable(BlobBuilder writer, ImmutableArray<int> stringMap, MetadataSizes metadataSizes)
         {
             foreach (var row in _localConstantTable)
             {
-                writer.WriteReference(SerializeHandle(row.Name), metadataSizes.StringReferenceIsSmall);
+                writer.WriteReference(SerializeHandle(stringMap, row.Name), metadataSizes.StringReferenceIsSmall);
                 writer.WriteReference(SerializeHandle(row.Signature), metadataSizes.BlobReferenceIsSmall);
             }
         }
diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataBuilder.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataBuilder.cs
new file mode 100644 (file)
index 0000000..8d438a0
--- /dev/null
@@ -0,0 +1,121 @@
+// 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.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection.Internal;
+
+namespace System.Reflection.Metadata.Ecma335
+{
+    public sealed partial class MetadataBuilder
+    {
+        internal SerializedMetadata GetSerializedMetadata(ImmutableArray<int> externalRowCounts, int metadataVersionByteCount, bool isStandaloneDebugMetadata)
+        {
+            var stringHeapBuilder = new HeapBlobBuilder(_stringHeapCapacity);
+            var stringMap = SerializeStringHeap(stringHeapBuilder, _strings, _stringHeapStartOffset);
+
+            Debug.Assert(HeapIndex.UserString == 0);
+            Debug.Assert((int)HeapIndex.String == 1);
+            Debug.Assert((int)HeapIndex.Blob == 2);
+            Debug.Assert((int)HeapIndex.Guid == 3);
+
+            var heapSizes = ImmutableArray.Create(
+                _userStringBuilder.Count,
+                stringHeapBuilder.Count,
+                _blobHeapSize,
+                _guidBuilder.Count);
+
+            var sizes = new MetadataSizes(GetRowCounts(), externalRowCounts, heapSizes, metadataVersionByteCount, isStandaloneDebugMetadata);
+
+            return new SerializedMetadata(sizes, stringHeapBuilder, stringMap);
+        }
+
+        internal static void SerializeMetadataHeader(BlobBuilder builder, string metadataVersion, MetadataSizes sizes)
+        {
+            int startOffset = builder.Count;
+
+            // signature
+            builder.WriteUInt32(0x424A5342);
+
+            // major version
+            builder.WriteUInt16(1);
+
+            // minor version
+            builder.WriteUInt16(1);
+
+            // reserved
+            builder.WriteUInt32(0);
+
+            // Spec (section 24.2.1 Metadata Root):
+            // Length ... Number of bytes allocated to hold version string (including null terminator), call this x.
+            //            Call the length of the string (including the terminator) m (we require m <= 255);
+            //            the length x is m rounded up to a multiple of four.
+            builder.WriteInt32(sizes.MetadataVersionPaddedLength);
+
+            int metadataVersionStart = builder.Count;
+            builder.WriteUTF8(metadataVersion);
+            builder.WriteByte(0);
+            int metadataVersionEnd = builder.Count;
+
+            for (int i = 0; i < sizes.MetadataVersionPaddedLength - (metadataVersionEnd - metadataVersionStart); i++)
+            {
+                builder.WriteByte(0);
+            }
+
+            // reserved
+            builder.WriteUInt16(0);
+
+            // number of streams
+            builder.WriteUInt16((ushort)(5 + (sizes.IsEncDelta ? 1 : 0) + (sizes.IsStandaloneDebugMetadata ? 1 : 0)));
+
+            // stream headers
+            int offsetFromStartOfMetadata = sizes.MetadataHeaderSize;
+
+            // emit the #Pdb stream first so that only a single page has to be read in order to find out PDB ID
+            if (sizes.IsStandaloneDebugMetadata)
+            {
+                SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.StandalonePdbStreamSize, "#Pdb", builder);
+            }
+
+            // Spec: Some compilers store metadata in a #- stream, which holds an uncompressed, or non-optimized, representation of metadata tables;
+            // this includes extra metadata -Ptr tables. Such PE files do not form part of ECMA-335 standard.
+            //
+            // Note: EnC delta is stored as uncompressed metadata stream.
+            SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.MetadataTableStreamSize, (sizes.IsCompressed ? "#~" : "#-"), builder);
+
+            SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.String), "#Strings", builder);
+            SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.UserString), "#US", builder);
+            SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.Guid), "#GUID", builder);
+            SerializeStreamHeader(ref offsetFromStartOfMetadata, sizes.GetAlignedHeapSize(HeapIndex.Blob), "#Blob", builder);
+
+            if (sizes.IsEncDelta)
+            {
+                SerializeStreamHeader(ref offsetFromStartOfMetadata, 0, "#JTD", builder);
+            }
+
+            int endOffset = builder.Count;
+            Debug.Assert(endOffset - startOffset == sizes.MetadataHeaderSize);
+        }
+
+        private static void SerializeStreamHeader(ref int offsetFromStartOfMetadata, int alignedStreamSize, string streamName, BlobBuilder builder)
+        {
+            // 4 for the first uint (offset), 4 for the second uint (padded size), length of stream name + 1 for null terminator (then padded)
+            int sizeOfStreamHeader = MetadataSizes.GetMetadataStreamHeaderSize(streamName);
+            builder.WriteInt32(offsetFromStartOfMetadata);
+            builder.WriteInt32(alignedStreamSize);
+            foreach (char ch in streamName)
+            {
+                builder.WriteByte((byte)ch);
+            }
+
+            // After offset, size, and stream name, write 0-bytes until we reach our padded size.
+            for (uint i = 8 + (uint)streamName.Length; i < sizeOfStreamHeader; i++)
+            {
+                builder.WriteByte(0);
+            }
+
+            offsetFromStartOfMetadata += alignedStreamSize;
+        }
+    }
+}
index 6f804ab..f57972a 100644 (file)
@@ -25,7 +25,7 @@ namespace System.Reflection.Metadata.Ecma335
                 Throw.ArgumentNull(nameof(reader));
             }
 
-            if ((int)tableIndex >= TableIndexExtensions.Count)
+            if ((int)tableIndex >= MetadataTokens.TableCount)
             {
                 Throw.TableIndexOutOfRange();
             }
diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataRootBuilder.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataRootBuilder.cs
new file mode 100644 (file)
index 0000000..0371470
--- /dev/null
@@ -0,0 +1,110 @@
+// 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.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+
+namespace System.Reflection.Metadata.Ecma335
+{
+    /// <summary>
+    /// Builder of a Metadata Root to be embedded in a Portable Executable image.
+    /// </summary>
+    /// <remarks>
+    /// Metadata root constitutes of a metadata header followed by metadata streams (#~, #Strings, #US, #Guid and #Blob).
+    /// </remarks>
+    public sealed class MetadataRootBuilder
+    {
+        private const string DefaultMetadataVersionString = "v4.0.30319";
+
+        // internal for testing
+        internal static readonly ImmutableArray<int> EmptyRowCounts = ImmutableArray.CreateRange(Enumerable.Repeat(0, MetadataTokens.TableCount));
+
+        private readonly MetadataBuilder _tablesAndHeaps;
+        private readonly SerializedMetadata _serializedMetadata;
+
+        /// <summary>
+        /// Creates a builder of a metadata root.
+        /// </summary>
+        /// <param name="tablesAndHeaps">
+        /// Builder populated with metadata entities stored in tables and values stored in heaps.
+        /// The entities and values will be enumerated when serializing the metadata root.
+        /// </param>
+        /// <param name="metadataVersion">
+        /// The version string written to the metadata header. The default value is "v4.0.30319".
+        /// </param>
+        /// <exception cref="ArgumentNullException"><paramref name="tablesAndHeaps"/> is null.</exception>
+        /// <exception cref="ArgumentException"><paramref name="metadataVersion"/> is too long (the number of bytes when UTF8-encoded must be less than 255).</exception>
+        public MetadataRootBuilder(MetadataBuilder tablesAndHeaps, string metadataVersion = null)
+        {
+            if (tablesAndHeaps == null)
+            {
+                Throw.ArgumentNull(nameof(tablesAndHeaps));
+            }
+
+            Debug.Assert(BlobUtilities.GetUTF8ByteCount(DefaultMetadataVersionString) == DefaultMetadataVersionString.Length);
+            int metadataVersionByteCount = metadataVersion != null ? BlobUtilities.GetUTF8ByteCount(metadataVersion) : DefaultMetadataVersionString.Length;
+
+            if (metadataVersionByteCount > MetadataSizes.MaxMetadataVersionByteCount)
+            {
+                Throw.InvalidArgument(SR.MetadataVersionTooLong, nameof(metadataVersion));
+            }
+
+            _tablesAndHeaps = tablesAndHeaps;
+            MetadataVersion = metadataVersion ?? DefaultMetadataVersionString;
+            _serializedMetadata = tablesAndHeaps.GetSerializedMetadata(EmptyRowCounts, metadataVersionByteCount, isStandaloneDebugMetadata: false);
+        }
+
+        /// <summary>
+        /// Metadata version string.
+        /// </summary>
+        public string MetadataVersion { get; }
+
+        /// <summary>
+        /// Returns sizes of various metadata structures.
+        /// </summary>
+        public MetadataSizes Sizes => _serializedMetadata.Sizes;
+
+        /// <summary>
+        /// Serialized the metadata root content into the given <see cref="BlobBuilder"/>.
+        /// </summary>
+        /// <param name="builder">Builder to write to.</param>
+        /// <param name="methodBodyStreamRva">
+        /// The relative virtual address of the start of the method body stream.
+        /// Used to calculate the final value of RVA fields of MethodDef table.
+        /// </param>
+        /// <param name="mappedFieldDataStreamRva">
+        /// The relative virtual address of the start of the field init data stream.
+        /// Used to calculate the final value of RVA fields of FieldRVA table.
+        /// </param>
+        /// <exception cref="ArgumentNullException"><paramref name="builder"/> is null.</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="methodBodyStreamRva"/> or <paramref name="mappedFieldDataStreamRva"/> is negative.</exception>
+        public void Serialize(BlobBuilder builder, int methodBodyStreamRva, int mappedFieldDataStreamRva)
+        {
+            if (builder == null)
+            {
+                Throw.ArgumentNull(nameof(builder));
+            }
+
+            if (methodBodyStreamRva < 0)
+            {
+                Throw.ArgumentOutOfRange(nameof(methodBodyStreamRva));
+            }
+
+            if (mappedFieldDataStreamRva < 0)
+            {
+                Throw.ArgumentOutOfRange(nameof(mappedFieldDataStreamRva));
+            }
+
+            // header:
+            MetadataBuilder.SerializeMetadataHeader(builder, MetadataVersion, _serializedMetadata.Sizes);
+
+            // #~ or #- stream:
+            _tablesAndHeaps.SerializeMetadataTables(builder, _serializedMetadata.Sizes, _serializedMetadata.StringMap, methodBodyStreamRva, mappedFieldDataStreamRva);
+
+            // #Strings, #US, #Guid and #Blob streams:
+            _tablesAndHeaps.WriteHeapsTo(builder, _serializedMetadata.StringHeap);
+        }
+    }
+}
diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSerializer.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSerializer.cs
deleted file mode 100644 (file)
index 014fb67..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-// 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.Collections.Generic;
-using System.Collections.Immutable;
-using System.Diagnostics;
-using System.Linq;
-using System.Reflection.PortableExecutable;
-
-namespace System.Reflection.Metadata.Ecma335
-{
-    public sealed class StandaloneDebugMetadataSerializer : MetadataSerializer
-    {
-        private const string DebugMetadataVersionString = "PDB v1.0";
-        public ushort FormatVersion => 0x0100;
-
-        private Blob _pdbIdBlob;
-        private readonly MethodDefinitionHandle _entryPoint;
-        public Func<IEnumerable<Blob>, BlobContentId> IdProvider { get; }
-
-        public StandaloneDebugMetadataSerializer(
-            MetadataBuilder builder, 
-            ImmutableArray<int> typeSystemRowCounts,
-            MethodDefinitionHandle entryPoint,
-            bool isMinimalDelta,
-            Func<IEnumerable<Blob>, BlobContentId> deterministicIdProvider = null)
-            : base(builder, CreateSizes(builder, typeSystemRowCounts, isMinimalDelta, isStandaloneDebugMetadata: true), DebugMetadataVersionString)
-        {
-            _entryPoint = entryPoint;
-            IdProvider = deterministicIdProvider ?? BlobContentId.GetTimeBasedProvider();
-        }
-
-        /// <summary>
-        /// Serialized #Pdb stream.
-        /// </summary>
-        protected override void SerializeStandalonePdbStream(BlobBuilder builder)
-        {
-            int startPosition = builder.Count;
-
-            // the id will be filled in later
-            _pdbIdBlob = builder.ReserveBytes(MetadataSizes.PdbIdSize);
-            
-            builder.WriteInt32(_entryPoint.IsNil ? 0 : MetadataTokens.GetToken(_entryPoint));
-
-            builder.WriteUInt64(MetadataSizes.ExternalTablesMask);
-            MetadataWriterUtilities.SerializeRowCounts(builder, MetadataSizes.ExternalRowCounts);
-
-            int endPosition = builder.Count;
-            Debug.Assert(MetadataSizes.CalculateStandalonePdbStreamSize() == endPosition - startPosition);
-        }
-
-        public void SerializeMetadata(BlobBuilder builder, out BlobContentId contentId)
-        {
-            SerializeMetadataImpl(builder, methodBodyStreamRva: 0, mappedFieldDataStreamRva: 0);
-
-            contentId = IdProvider(builder.GetBlobs());
-
-            // fill in the id:
-            var idWriter = new BlobWriter(_pdbIdBlob);
-            idWriter.WriteGuid(contentId.Guid);
-            idWriter.WriteUInt32(contentId.Stamp);
-            Debug.Assert(idWriter.RemainingBytes == 0);
-        }
-    }
-
-    public sealed class TypeSystemMetadataSerializer : MetadataSerializer
-    {
-        private static readonly ImmutableArray<int> EmptyRowCounts = ImmutableArray.CreateRange(Enumerable.Repeat(0, MetadataTokens.TableCount));
-
-        public TypeSystemMetadataSerializer(
-            MetadataBuilder builder, 
-            string metadataVersion,
-            bool isMinimalDelta)
-            : base(builder, CreateSizes(builder, EmptyRowCounts, isMinimalDelta, isStandaloneDebugMetadata: false), metadataVersion)
-        {
-            
-        }
-
-        protected override void SerializeStandalonePdbStream(BlobBuilder builder)
-        {
-            // nop
-        }
-
-        public void SerializeMetadata(BlobBuilder builder, int methodBodyStreamRva, int mappedFieldDataStreamRva)
-        {
-            SerializeMetadataImpl(builder, methodBodyStreamRva, mappedFieldDataStreamRva);
-        }
-    }
-
-    public abstract class MetadataSerializer
-    {
-        protected readonly MetadataBuilder _builder;
-        private readonly MetadataSizes _sizes;
-        private readonly string _metadataVersion;
-
-        public MetadataSerializer(MetadataBuilder builder, MetadataSizes sizes, string metadataVersion)
-        {
-            _builder = builder;
-            _sizes = sizes;
-            _metadataVersion = metadataVersion;
-        }
-
-        internal static MetadataSizes CreateSizes(MetadataBuilder builder, ImmutableArray<int> externalRowCounts, bool isMinimalDelta, bool isStandaloneDebugMetadata)
-        {
-            builder.CompleteHeaps();
-
-            return new MetadataSizes(
-                builder.GetRowCounts(),
-                externalRowCounts,
-                builder.GetHeapSizes(),
-                isMinimalDelta,
-                isStandaloneDebugMetadata);
-        }
-
-        protected abstract void SerializeStandalonePdbStream(BlobBuilder builder);
-
-        public MetadataSizes MetadataSizes => _sizes;
-
-        protected void SerializeMetadataImpl(BlobBuilder builder, int methodBodyStreamRva, int mappedFieldDataStreamRva)
-        {
-            // header:
-            SerializeMetadataHeader(builder);
-
-            // #Pdb stream
-            SerializeStandalonePdbStream(builder);
-            
-            // #~ or #- stream:
-            _builder.SerializeMetadataTables(builder, _sizes, methodBodyStreamRva, mappedFieldDataStreamRva);
-
-            // #Strings, #US, #Guid and #Blob streams:
-            _builder.WriteHeapsTo(builder);
-        }
-
-        private void SerializeMetadataHeader(BlobBuilder builder)
-        {
-            int startOffset = builder.Count;
-
-            // signature
-            builder.WriteUInt32(0x424A5342);
-
-            // major version
-            builder.WriteUInt16(1);
-
-            // minor version
-            builder.WriteUInt16(1);
-
-            // reserved
-            builder.WriteUInt32(0);
-
-            // metadata version length
-            builder.WriteUInt32(MetadataSizes.MetadataVersionPaddedLength);
-
-            int n = Math.Min(MetadataSizes.MetadataVersionPaddedLength, _metadataVersion.Length);
-            for (int i = 0; i < n; i++)
-            {
-                builder.WriteByte((byte)_metadataVersion[i]);
-            }
-
-            for (int i = n; i < MetadataSizes.MetadataVersionPaddedLength; i++)
-            {
-                builder.WriteByte(0);
-            }
-
-            // reserved
-            builder.WriteUInt16(0);
-
-            // number of streams
-            builder.WriteUInt16((ushort)(5 + (_sizes.IsMinimalDelta ? 1 : 0) + (_sizes.IsStandaloneDebugMetadata ? 1 : 0)));
-
-            // stream headers
-            int offsetFromStartOfMetadata = _sizes.MetadataHeaderSize;
-
-            // emit the #Pdb stream first so that only a single page has to be read in order to find out PDB ID
-            if (_sizes.IsStandaloneDebugMetadata)
-            {
-                SerializeStreamHeader(ref offsetFromStartOfMetadata, _sizes.StandalonePdbStreamSize, "#Pdb", builder);
-            }
-
-            // Spec: Some compilers store metadata in a #- stream, which holds an uncompressed, or non-optimized, representation of metadata tables;
-            // this includes extra metadata -Ptr tables. Such PE files do not form part of ECMA-335 standard.
-            //
-            // Note: EnC delta is stored as uncompressed metadata stream.
-            SerializeStreamHeader(ref offsetFromStartOfMetadata, _sizes.MetadataTableStreamSize, (_sizes.IsMetadataTableStreamCompressed ? "#~" : "#-"), builder);
-
-            SerializeStreamHeader(ref offsetFromStartOfMetadata, _sizes.GetAlignedHeapSize(HeapIndex.String), "#Strings", builder);
-            SerializeStreamHeader(ref offsetFromStartOfMetadata, _sizes.GetAlignedHeapSize(HeapIndex.UserString), "#US", builder);
-            SerializeStreamHeader(ref offsetFromStartOfMetadata, _sizes.GetAlignedHeapSize(HeapIndex.Guid), "#GUID", builder);
-            SerializeStreamHeader(ref offsetFromStartOfMetadata, _sizes.GetAlignedHeapSize(HeapIndex.Blob), "#Blob", builder);
-
-            if (_sizes.IsMinimalDelta)
-            {
-                SerializeStreamHeader(ref offsetFromStartOfMetadata, 0, "#JTD", builder);
-            }
-
-            int endOffset = builder.Count;
-            Debug.Assert(endOffset - startOffset == _sizes.MetadataHeaderSize);
-        }
-
-        private static void SerializeStreamHeader(ref int offsetFromStartOfMetadata, int alignedStreamSize, string streamName, BlobBuilder builder)
-        {
-            // 4 for the first uint (offset), 4 for the second uint (padded size), length of stream name + 1 for null terminator (then padded)
-            int sizeOfStreamHeader = MetadataSizes.GetMetadataStreamHeaderSize(streamName);
-            builder.WriteInt32(offsetFromStartOfMetadata);
-            builder.WriteInt32(alignedStreamSize);
-            foreach (char ch in streamName)
-            {
-                builder.WriteByte((byte)ch);
-            }
-
-            // After offset, size, and stream name, write 0-bytes until we reach our padded size.
-            for (uint i = 8 + (uint)streamName.Length; i < sizeOfStreamHeader; i++)
-            {
-                builder.WriteByte(0);
-            }
-
-            offsetFromStartOfMetadata += alignedStreamSize;
-        }
-    }
-}
index 9770d25..34c3df3 100644 (file)
@@ -8,19 +8,25 @@ using System.Reflection.Internal;
 
 namespace System.Reflection.Metadata.Ecma335
 {
+    /// <summary>
+    /// Provides information on sizes of various metadata structures.
+    /// </summary>
     public sealed class MetadataSizes
     {
         private const int StreamAlignment = 4;
 
+        // Call the length of the string (including the terminator) m (we require m <= 255);
+        internal const int MaxMetadataVersionByteCount = 0xff - 1;
+
+        internal readonly int MetadataVersionPaddedLength;
+
         internal const ulong SortedDebugTables =
             1UL << (int)TableIndex.LocalScope |
             1UL << (int)TableIndex.StateMachineMethod |
             1UL << (int)TableIndex.CustomDebugInformation;
 
-        internal readonly bool IsMinimalDelta;
-
-        // EnC delta tables are stored as uncompressed metadata table stream
-        internal bool IsMetadataTableStreamCompressed => !IsMinimalDelta;
+        internal readonly bool IsEncDelta;
+        internal readonly bool IsCompressed;
 
         internal readonly bool BlobReferenceIsSmall;
         internal readonly bool StringReferenceIsSmall;
@@ -100,7 +106,7 @@ namespace System.Reflection.Metadata.Ecma335
             ImmutableArray<int> rowCounts,
             ImmutableArray<int> externalRowCounts,
             ImmutableArray<int> heapSizes,
-            bool isMinimalDelta,
+            int metadataVersionByteCount,
             bool isStandaloneDebugMetadata)
         {
             Debug.Assert(rowCounts.Length == MetadataTokens.TableCount);
@@ -110,15 +116,26 @@ namespace System.Reflection.Metadata.Ecma335
             RowCounts = rowCounts;
             ExternalRowCounts = externalRowCounts;
             HeapSizes = heapSizes;
-            IsMinimalDelta = isMinimalDelta;
 
-            BlobReferenceIsSmall = !isMinimalDelta && heapSizes[(int)HeapIndex.Blob] <= ushort.MaxValue;
-            StringReferenceIsSmall = !isMinimalDelta && heapSizes[(int)HeapIndex.String] <= ushort.MaxValue;
-            GuidReferenceIsSmall = !isMinimalDelta && heapSizes[(int)HeapIndex.Guid] <= ushort.MaxValue;
+            // +1 for NUL terminator
+            MetadataVersionPaddedLength = BitArithmetic.Align(metadataVersionByteCount + 1, 4);
 
             PresentTablesMask = ComputeNonEmptyTableMask(rowCounts);
             ExternalTablesMask = ComputeNonEmptyTableMask(externalRowCounts);
 
+            // Auto-detect EnC delta from presence of EnC tables.
+            // EnC delta tables are stored as uncompressed metadata table stream, other metadata use compressed stream.
+            // We could support uncompress non-EnC metadata in future if we needed to.
+            bool isEncDelta = IsPresent(TableIndex.EncLog) || IsPresent(TableIndex.EncMap);
+            bool isCompressed = !isEncDelta;
+
+            IsEncDelta = isEncDelta;
+            IsCompressed = isCompressed;
+
+            BlobReferenceIsSmall = isCompressed && heapSizes[(int)HeapIndex.Blob] <= ushort.MaxValue;
+            StringReferenceIsSmall = isCompressed && heapSizes[(int)HeapIndex.String] <= ushort.MaxValue;
+            GuidReferenceIsSmall = isCompressed && heapSizes[(int)HeapIndex.Guid] <= ushort.MaxValue;
+
             // table can either be present or external, it can't be both:
             Debug.Assert((PresentTablesMask & ExternalTablesMask) == 0);
 
@@ -324,7 +341,7 @@ namespace System.Reflection.Metadata.Ecma335
             get
             {
                 const int RegularStreamHeaderSizes = 76;
-                const int MinimalDeltaMarkerStreamHeaderSize = 16;
+                const int EncDeltaMarkerStreamHeaderSize = 16;
                 const int StandalonePdbStreamHeaderSize = 16;
 
                 Debug.Assert(RegularStreamHeaderSizes ==
@@ -334,7 +351,7 @@ namespace System.Reflection.Metadata.Ecma335
                     GetMetadataStreamHeaderSize("#GUID") +
                     GetMetadataStreamHeaderSize("#Blob"));
 
-                Debug.Assert(MinimalDeltaMarkerStreamHeaderSize == GetMetadataStreamHeaderSize("#JTD"));
+                Debug.Assert(EncDeltaMarkerStreamHeaderSize == GetMetadataStreamHeaderSize("#JTD"));
                 Debug.Assert(StandalonePdbStreamHeaderSize == GetMetadataStreamHeaderSize("#Pdb"));
 
                 return
@@ -348,13 +365,10 @@ namespace System.Reflection.Metadata.Ecma335
                     sizeof(ushort) +               // stream count
                     (IsStandaloneDebugMetadata ? StandalonePdbStreamHeaderSize : 0) + 
                     RegularStreamHeaderSizes +
-                    (IsMinimalDelta ? MinimalDeltaMarkerStreamHeaderSize : 0);
+                    (IsEncDelta ? EncDeltaMarkerStreamHeaderSize : 0);
             }
         }
 
-        // version must be 12 chars long, this observation is not supported by the standard
-        internal const int MetadataVersionPaddedLength = 12;
-
         internal static int GetMetadataStreamHeaderSize(string streamName)
         {
             return
@@ -439,7 +453,7 @@ namespace System.Reflection.Metadata.Ecma335
         private bool IsReferenceSmall(int tagBitSize, params TableIndex[] tables)
         {
             const int smallBitCount = 16;
-            return IsMetadataTableStreamCompressed && ReferenceFits(smallBitCount - tagBitSize, tables);
+            return IsCompressed && ReferenceFits(smallBitCount - tagBitSize, tables);
         }
 
         private bool ReferenceFits(int bitCount, TableIndex[] tables)
index 60f0c85..b86fcea 100644 (file)
@@ -9,7 +9,7 @@ namespace System.Reflection.Metadata.Ecma335
         /// <summary>
         /// Maximum number of tables that can be present in Ecma335 metadata.
         /// </summary>
-        public static readonly int TableCount = TableIndexExtensions.Count;
+        public static readonly int TableCount = 64; // headers use 64bit table masks
 
         /// <summary>
         /// Maximum number of tables that can be present in Ecma335 metadata.
@@ -232,10 +232,12 @@ namespace System.Reflection.Metadata.Ecma335
         /// </summary>
         /// <param name="type">Handle type.</param>
         /// <param name="index">Table index.</param>
-        /// <returns>True if the handle type corresponds to an Ecma335 table, false otherwise.</returns>
+        /// <returns>True if the handle type corresponds to an Ecma335 or Portable PDB table, false otherwise.</returns>
         public static bool TryGetTableIndex(HandleKind type, out TableIndex index)
         {
-            if ((int)type < TableIndexExtensions.Count)
+            // We don't have a HandleKind for PropertyMap, EventMap, MethodSemantics, *Ptr, AssemblyOS, etc. tables, 
+            // but one can get ahold of one by creating a handle from a token and getting its Kind.
+            if ((int)type < TableCount && ((1UL << (int)type) & (ulong)TableMask.AllTables) != 0)
             {
                 index = (TableIndex)type;
                 return true;
diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/PortablePdbBuilder.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/PortablePdbBuilder.cs
new file mode 100644 (file)
index 0000000..a329731
--- /dev/null
@@ -0,0 +1,157 @@
+// 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.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+
+namespace System.Reflection.Metadata.Ecma335
+{
+    /// <summary>
+    /// Builder of a Portable PDB image.
+    /// </summary>
+    public sealed class PortablePdbBuilder
+    {
+        public string MetadataVersion => "PDB v1.0";
+        public ushort FormatVersion => 0x0100;
+
+        private Blob _pdbIdBlob;
+        private readonly MethodDefinitionHandle _entryPoint;
+        private readonly MetadataBuilder _builder;
+        private readonly SerializedMetadata _serializedMetadata;
+
+        public Func<IEnumerable<Blob>, BlobContentId> IdProvider { get; }
+
+        /// <summary>
+        /// Creates a builder of a Portable PDB image.
+        /// </summary>
+        /// <param name="tablesAndHeaps">
+        /// Builder populated with debug metadata entities stored in tables and values stored in heaps.
+        /// The entities and values will be enumerated when serializing the Portable PDB image.
+        /// </param>
+        /// <param name="typeSystemRowCounts">
+        /// Row counts of all tables that the associated type-system metadata contain.
+        /// Each slot in the array corresponds to a table (<see cref="TableIndex"/>).
+        /// The length of the array must be equal to <see cref="MetadataTokens.TableCount"/>.
+        /// </param>
+        /// <param name="entryPoint">
+        /// Entry point method definition handle.
+        /// </param>
+        /// <param name="idProvider">
+        /// Function calculating id of content represented as a sequence of blobs.
+        /// If not specified a default function that ignores the content and returns current time-based content id is used 
+        /// (<see cref="BlobContentId.GetTimeBasedProvider()"/>).
+        /// You must specify a deterministic function to produce a deterministic Portable PDB image.
+        /// </param>
+        /// <exception cref="ArgumentNullException"><paramref name="tablesAndHeaps"/> or <paramref name="typeSystemRowCounts"/> is null.</exception>
+        public PortablePdbBuilder(
+            MetadataBuilder tablesAndHeaps, 
+            ImmutableArray<int> typeSystemRowCounts,
+            MethodDefinitionHandle entryPoint,
+            Func<IEnumerable<Blob>, BlobContentId> idProvider = null)
+        {
+            if (tablesAndHeaps == null)
+            {
+                Throw.ArgumentNull(nameof(tablesAndHeaps));
+            }
+
+            ValidateTypeSystemRowCounts(typeSystemRowCounts);
+            
+            _builder = tablesAndHeaps;
+            _entryPoint = entryPoint;
+
+            Debug.Assert(BlobUtilities.GetUTF8ByteCount(MetadataVersion) == MetadataVersion.Length);
+            _serializedMetadata = tablesAndHeaps.GetSerializedMetadata(typeSystemRowCounts, MetadataVersion.Length, isStandaloneDebugMetadata: true);
+
+            IdProvider = idProvider ?? BlobContentId.GetTimeBasedProvider();
+        }
+
+        private static void ValidateTypeSystemRowCounts(ImmutableArray<int> typeSystemRowCounts)
+        {
+            if (typeSystemRowCounts.IsDefault)
+            {
+                Throw.ArgumentNull(nameof(typeSystemRowCounts));
+            }
+
+            if (typeSystemRowCounts.Length != MetadataTokens.TableCount)
+            {
+                throw new ArgumentException(SR.Format(SR.ExpectedArrayOfSize, MetadataTokens.TableCount), nameof(typeSystemRowCounts));
+            }
+
+            for (int i = 0; i < typeSystemRowCounts.Length; i++)
+            {
+                if (typeSystemRowCounts[i] == 0)
+                {
+                    continue;
+                }
+
+                if ((unchecked((uint)typeSystemRowCounts[i]) & ~TokenTypeIds.RIDMask) != 0)
+                {
+                    throw new ArgumentOutOfRangeException(nameof(typeSystemRowCounts), SR.Format(SR.RowCountOutOfRange, i));
+                }
+
+                if (((1UL << i) & (ulong)TableMask.ValidPortablePdbExternalTables) == 0)
+                {
+                    throw new ArgumentException(SR.Format(SR.RowCountMustBeZero, i), nameof(typeSystemRowCounts));
+                }
+            }
+        }
+
+        /// <summary>
+        /// Serialized #Pdb stream.
+        /// </summary>
+        private void SerializeStandalonePdbStream(BlobBuilder builder)
+        {
+            int startPosition = builder.Count;
+
+            // the id will be filled in later
+            _pdbIdBlob = builder.ReserveBytes(MetadataSizes.PdbIdSize);
+            
+            builder.WriteInt32(_entryPoint.IsNil ? 0 : MetadataTokens.GetToken(_entryPoint));
+
+            builder.WriteUInt64(_serializedMetadata.Sizes.ExternalTablesMask);
+            MetadataWriterUtilities.SerializeRowCounts(builder, _serializedMetadata.Sizes.ExternalRowCounts);
+
+            int endPosition = builder.Count;
+            Debug.Assert(_serializedMetadata.Sizes.CalculateStandalonePdbStreamSize() == endPosition - startPosition);
+        }
+
+        /// <summary>
+        /// Serializes Portable PDB content into the given <see cref="BlobBuilder"/>.
+        /// </summary>
+        /// <param name="builder">Builder to write to.</param>
+        /// <returns>The id of the serialized content.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="builder"/> is null.</exception>
+        public BlobContentId Serialize(BlobBuilder builder)
+        {
+            if (builder == null)
+            {
+                Throw.ArgumentNull(nameof(builder));
+            }
+
+            // header:
+            MetadataBuilder.SerializeMetadataHeader(builder, MetadataVersion, _serializedMetadata.Sizes);
+
+            // #Pdb stream
+            SerializeStandalonePdbStream(builder);
+
+            // #~ or #- stream:
+            _builder.SerializeMetadataTables(builder, _serializedMetadata.Sizes, _serializedMetadata.StringMap, methodBodyStreamRva: 0, mappedFieldDataStreamRva: 0);
+
+            // #Strings, #US, #Guid and #Blob streams:
+            _builder.WriteHeapsTo(builder, _serializedMetadata.StringHeap);
+
+            var contentId = IdProvider(builder.GetBlobs());
+
+            // fill in the id:
+            var idWriter = new BlobWriter(_pdbIdBlob);
+            idWriter.WriteGuid(contentId.Guid);
+            idWriter.WriteUInt32(contentId.Stamp);
+            Debug.Assert(idWriter.RemainingBytes == 0);
+
+            return contentId;
+        }
+    }
+}
diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/SerializedMetadataHeaps.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/SerializedMetadataHeaps.cs
new file mode 100644 (file)
index 0000000..63ff6ea
--- /dev/null
@@ -0,0 +1,25 @@
+// 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.Collections.Immutable;
+
+namespace System.Reflection.Metadata.Ecma335
+{
+    internal sealed class SerializedMetadata
+    {
+        internal readonly ImmutableArray<int> StringMap;
+        internal readonly BlobBuilder StringHeap;
+        internal readonly MetadataSizes Sizes;
+
+        public SerializedMetadata(
+            MetadataSizes sizes,
+            BlobBuilder stringHeap,
+            ImmutableArray<int> stringMap)
+        {
+            Sizes = sizes;
+            StringHeap = stringHeap;
+            StringMap = stringMap;
+        }
+    }
+}
index 0553ccc..13e38b3 100644 (file)
@@ -62,9 +62,4 @@ namespace System.Reflection.Metadata.Ecma335
         StateMachineMethod = 0x36,
         CustomDebugInformation = 0x37,
     }
-
-    internal static class TableIndexExtensions
-    {
-        internal const int Count = (int)TableIndex.CustomDebugInformation + 1;
-    }
 }
index 40f4dfe..615736f 100644 (file)
@@ -9,7 +9,7 @@ namespace System.Reflection.Metadata.Ecma335
         StringHeapLarge = 0x01, // 4 byte uint indexes used for string heap offsets
         GuidHeapLarge = 0x02,   // 4 byte uint indexes used for GUID heap offsets
         BlobHeapLarge = 0x04,   // 4 byte uint indexes used for Blob heap offsets
-        EnCDeltas = 0x20,       // Indicates only EnC Deltas are present
+        EncDeltas = 0x20,       // Indicates only EnC Deltas are present
         DeletedMarks = 0x80,    // Indicates metadata might contain items marked deleted
     }
 }
index 2b7b4a5..0ff777a 100644 (file)
@@ -78,15 +78,18 @@ namespace System.Reflection.Metadata.Ecma335
           | EventPtr
           | PropertyPtr,
 
-        V2_0_TablesMask =
-            Module
+        EncTables =
+            EnCLog
+          | EnCMap,
+
+        TypeSystemTables =
+            PtrTables 
+          | EncTables
+          | Module
           | TypeRef
           | TypeDef
-          | FieldPtr
           | Field
-          | MethodPtr
           | MethodDef
-          | ParamPtr
           | Param
           | InterfaceImpl
           | MemberRef
@@ -98,10 +101,8 @@ namespace System.Reflection.Metadata.Ecma335
           | FieldLayout
           | StandAloneSig
           | EventMap
-          | EventPtr
           | Event
           | PropertyMap
-          | PropertyPtr
           | Property
           | MethodSemantics
           | MethodImpl
@@ -109,8 +110,6 @@ namespace System.Reflection.Metadata.Ecma335
           | TypeSpec
           | ImplMap
           | FieldRva
-          | EnCLog
-          | EnCMap
           | Assembly
           | AssemblyRef
           | File
@@ -121,7 +120,7 @@ namespace System.Reflection.Metadata.Ecma335
           | MethodSpec
           | GenericParamConstraint,
 
-        PortablePdb_TablesMask =
+        DebugTables =
             Document
           | MethodDebugInformation
           | LocalScope
@@ -131,9 +130,12 @@ namespace System.Reflection.Metadata.Ecma335
           | StateMachineMethod
           | CustomDebugInformation,
 
-        V3_0_TablesMask =
-            V2_0_TablesMask
-          | PortablePdb_TablesMask,
+        AllTables = 
+            TypeSystemTables |
+            DebugTables,
+
+        ValidPortablePdbExternalTables =
+            TypeSystemTables & ~PtrTables & ~EncTables
     }
 
     internal enum HeapSizes : byte
index 2c149c4..b97f95b 100644 (file)
@@ -464,7 +464,7 @@ namespace System.Reflection.Metadata
             // We will not be checking version values. We will continue checking that the set of 
             // present tables is within the set we understand.
 
-            ulong validTables = (ulong)TableMask.V3_0_TablesMask;
+            ulong validTables = (ulong)(TableMask.TypeSystemTables | TableMask.DebugTables);
 
             if ((presentTables & ~validTables) != 0)
             {
@@ -497,7 +497,7 @@ namespace System.Reflection.Metadata
         {
             ulong currentTableBit = 1;
 
-            var rowCounts = new int[TableIndexExtensions.Count];
+            var rowCounts = new int[MetadataTokens.TableCount];
             for (int i = 0; i < rowCounts.Length; i++)
             {
                 if ((presentTableMask & currentTableBit) != 0)
@@ -546,7 +546,7 @@ namespace System.Reflection.Metadata
             ulong externalTableMask = reader.ReadUInt64();
 
             // EnC & Ptr tables can't be referenced from standalone PDB metadata:
-            const ulong validTables = (ulong)(TableMask.V2_0_TablesMask & ~TableMask.PtrTables & ~TableMask.EnCLog & ~TableMask.EnCMap);
+            const ulong validTables = (ulong)TableMask.ValidPortablePdbExternalTables;
 
             if ((externalTableMask & ~validTables) != 0)
             {
@@ -812,7 +812,7 @@ namespace System.Reflection.Metadata
 
             bool isAllReferencedTablesSmall = true;
             ulong tablesReferencedMask = (ulong)tablesReferenced;
-            for (int tableIndex = 0; tableIndex < TableIndexExtensions.Count; tableIndex++)
+            for (int tableIndex = 0; tableIndex < MetadataTokens.TableCount; tableIndex++)
             {
                 if ((tablesReferencedMask & 1) != 0)
                 {
index 37bd4a5..0921561 100644 (file)
@@ -22,7 +22,7 @@ namespace System.Reflection.PortableExecutable
         private const string RelocationSectionName = ".reloc";
 
         private readonly PEDirectoriesBuilder _peDirectoriesBuilder;
-        private readonly TypeSystemMetadataSerializer _metadataSerializer;
+        private readonly MetadataRootBuilder _metadataRootBuilder;
         private readonly BlobBuilder _ilStream;
         private readonly BlobBuilder _mappedFieldDataOpt;
         private readonly BlobBuilder _managedResourcesOpt;
@@ -37,7 +37,7 @@ namespace System.Reflection.PortableExecutable
 
         public ManagedPEBuilder(
             PEHeaderBuilder header,
-            TypeSystemMetadataSerializer metadataSerializer,
+            MetadataRootBuilder metadataRootBuilder,
             BlobBuilder ilStream,
             BlobBuilder mappedFieldData = null,
             BlobBuilder managedResources = null,
@@ -54,9 +54,9 @@ namespace System.Reflection.PortableExecutable
                 Throw.ArgumentNull(nameof(header));
             }
 
-            if (metadataSerializer == null)
+            if (metadataRootBuilder == null)
             {
-                Throw.ArgumentNull(nameof(metadataSerializer));
+                Throw.ArgumentNull(nameof(metadataRootBuilder));
             }
 
             if (ilStream == null)
@@ -69,7 +69,7 @@ namespace System.Reflection.PortableExecutable
                 Throw.ArgumentOutOfRange(nameof(strongNameSignatureSize));
             }
 
-            _metadataSerializer = metadataSerializer;
+            _metadataRootBuilder = metadataRootBuilder;
             _ilStream = ilStream;
             _mappedFieldDataOpt = mappedFieldData;
             _managedResourcesOpt = managedResources;
@@ -135,7 +135,7 @@ namespace System.Reflection.PortableExecutable
             var sectionBuilder = new BlobBuilder();
             var metadataBuilder = new BlobBuilder();
 
-            var metadataSizes = _metadataSerializer.MetadataSizes;
+            var metadataSizes = _metadataRootBuilder.Sizes;
 
             var textSection = new ManagedTextSection(
                 imageCharacteristics: Header.ImageCharacteristics,
@@ -149,7 +149,7 @@ namespace System.Reflection.PortableExecutable
 
             int methodBodyStreamRva = location.RelativeVirtualAddress + textSection.OffsetToILStream;
             int mappedFieldDataStreamRva = location.RelativeVirtualAddress + textSection.CalculateOffsetToMappedFieldDataStream();
-            _metadataSerializer.SerializeMetadata(metadataBuilder, methodBodyStreamRva, mappedFieldDataStreamRva);
+            _metadataRootBuilder.Serialize(metadataBuilder, methodBodyStreamRva, mappedFieldDataStreamRva);
 
             DirectoryEntry debugDirectoryEntry;
             BlobBuilder debugTableBuilderOpt;
index ec756b0..602d628 100644 (file)
@@ -25,6 +25,12 @@ namespace System.Reflection
         }
 
         [MethodImpl(MethodImplOptions.NoInlining)]
+        internal static void InvalidArgument(string message, string parameterName)
+        {
+            throw new ArgumentException(message, parameterName);
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
         internal static void InvalidArgument_OffsetForVirtualHeapHandle()
         {
             throw new ArgumentException(SR.CantGetOffsetForVirtualHeapHandle, "handle");
index f98180c..9fd1e8f 100644 (file)
@@ -29,147 +29,176 @@ namespace System.Reflection.Metadata.Ecma335.Tests
             var builder = new MetadataBuilder();
 
             builder.AddModule(default(int), default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.Module]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.Module));
 
             builder.AddAssembly(default(StringHandle), new Version(0, 0, 0, 0), default(StringHandle), default(BlobHandle), default(AssemblyFlags), default(AssemblyHashAlgorithm));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.Assembly]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.Assembly));
 
             var assemblyReference = builder.AddAssemblyReference(default(StringHandle), new Version(0, 0, 0, 0), default(StringHandle), default(BlobHandle), default(AssemblyFlags), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.AssemblyRef]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.AssemblyRef));
             Assert.Equal(1, MetadataTokens.GetRowNumber(assemblyReference));
 
             var typeDefinition = builder.AddTypeDefinition(default(TypeAttributes), default(StringHandle), default(StringHandle), default(EntityHandle), default(FieldDefinitionHandle), default(MethodDefinitionHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.TypeDef]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.TypeDef));
             Assert.Equal(1, MetadataTokens.GetRowNumber(typeDefinition));
 
             builder.AddTypeLayout(default(TypeDefinitionHandle), default(ushort), default(uint));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.ClassLayout]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.ClassLayout));
 
             builder.AddInterfaceImplementation(MetadataTokens.TypeDefinitionHandle(1), MetadataTokens.TypeDefinitionHandle(1));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.InterfaceImpl]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.InterfaceImpl));
 
             builder.AddNestedType(default(TypeDefinitionHandle), default(TypeDefinitionHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.NestedClass]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.NestedClass));
 
             var typeReference = builder.AddTypeReference(EntityHandle.ModuleDefinition, default(StringHandle), default(StringHandle));
             Assert.Equal(1, MetadataTokens.GetRowNumber(typeReference));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.TypeRef]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.TypeRef));
 
             builder.AddTypeSpecification(default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.TypeSpec]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.TypeSpec));
 
             builder.AddStandaloneSignature(default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.StandAloneSig]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.StandAloneSig));
 
             builder.AddProperty(default(PropertyAttributes), default(StringHandle), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.Property]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.Property));
 
             builder.AddPropertyMap(default(TypeDefinitionHandle), default(PropertyDefinitionHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.PropertyMap]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.PropertyMap));
 
             builder.AddEvent(default(EventAttributes), default(StringHandle), MetadataTokens.TypeDefinitionHandle(1));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.Event]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.Event));
 
             builder.AddEventMap(default(TypeDefinitionHandle), default(EventDefinitionHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.EventMap]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.EventMap));
 
             builder.AddConstant(MetadataTokens.FieldDefinitionHandle(1), default(object));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.Constant]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.Constant));
 
             builder.AddMethodSemantics(MetadataTokens.EventDefinitionHandle(1), default(ushort), default(MethodDefinitionHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.MethodSemantics]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.MethodSemantics));
 
             builder.AddCustomAttribute(MetadataTokens.TypeDefinitionHandle(1), MetadataTokens.MethodDefinitionHandle(1), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.CustomAttribute]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.CustomAttribute));
 
             builder.AddMethodSpecification(MetadataTokens.MethodDefinitionHandle(1), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.MethodSpec]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.MethodSpec));
 
             builder.AddModuleReference(default(StringHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.ModuleRef]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.ModuleRef));
 
             builder.AddParameter(default(ParameterAttributes), default(StringHandle), default(int));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.Param]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.Param));
 
             var genericParameter = builder.AddGenericParameter(MetadataTokens.MethodDefinitionHandle(1), default(GenericParameterAttributes), default(StringHandle), default(int));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.GenericParam]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.GenericParam));
             Assert.Equal(1, MetadataTokens.GetRowNumber(genericParameter));
 
             builder.AddGenericParameterConstraint(default(GenericParameterHandle), MetadataTokens.TypeDefinitionHandle(1));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.GenericParamConstraint]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.GenericParamConstraint));
 
             builder.AddFieldDefinition(default(FieldAttributes), default(StringHandle), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.Field]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.Field));
 
             builder.AddFieldLayout(default(FieldDefinitionHandle), default(int));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.FieldLayout]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.FieldLayout));
 
             builder.AddMarshallingDescriptor(MetadataTokens.FieldDefinitionHandle(1), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.FieldMarshal]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.FieldMarshal));
 
             builder.AddFieldRelativeVirtualAddress(default(FieldDefinitionHandle), default(int));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.FieldRva]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.FieldRva));
 
             var methodDefinition = builder.AddMethodDefinition(default(MethodAttributes), default(MethodImplAttributes), default(StringHandle), default(BlobHandle), default(int), default(ParameterHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.MethodDef]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.MethodDef));
             Assert.Equal(1, MetadataTokens.GetRowNumber(methodDefinition));
 
             builder.AddMethodImport(MetadataTokens.MethodDefinitionHandle(1), default(MethodImportAttributes), default(StringHandle), default(ModuleReferenceHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.ImplMap]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.ImplMap));
 
             builder.AddMethodImplementation(default(TypeDefinitionHandle), MetadataTokens.MethodDefinitionHandle(1), MetadataTokens.MethodDefinitionHandle(1));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.MethodImpl]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.MethodImpl));
 
             var memberReference = builder.AddMemberReference(MetadataTokens.TypeDefinitionHandle(1), default(StringHandle), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.MemberRef]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.MemberRef));
             Assert.Equal(1, MetadataTokens.GetRowNumber(memberReference));
 
             builder.AddManifestResource(default(ManifestResourceAttributes), default(StringHandle), MetadataTokens.AssemblyFileHandle(1), default(uint));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.ManifestResource]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.ManifestResource));
 
             builder.AddAssemblyFile(default(StringHandle), default(BlobHandle), default(Boolean));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.File]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.File));
 
             builder.AddExportedType(default(TypeAttributes), default(StringHandle), default(StringHandle), MetadataTokens.AssemblyFileHandle(1), default(int));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.ExportedType]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.ExportedType));
 
             builder.AddDeclarativeSecurityAttribute(MetadataTokens.TypeDefinitionHandle(1), default(DeclarativeSecurityAction), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.DeclSecurity]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.DeclSecurity));
 
             builder.AddEncLogEntry(MetadataTokens.TypeDefinitionHandle(1), default(EditAndContinueOperation));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.EncLog]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.EncLog));
 
             builder.AddEncMapEntry(MetadataTokens.TypeDefinitionHandle(1));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.EncMap]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.EncMap));
 
             var document = builder.AddDocument(default(BlobHandle), default(GuidHandle), default(BlobHandle), default(GuidHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.Document]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.Document));
             Assert.Equal(1, MetadataTokens.GetRowNumber(document));
 
             builder.AddMethodDebugInformation(default(DocumentHandle), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.MethodDebugInformation]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.MethodDebugInformation));
 
             var localScope = builder.AddLocalScope(default(MethodDefinitionHandle), default(ImportScopeHandle), default(LocalVariableHandle), default(LocalConstantHandle), default(int), default(int));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.LocalScope]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.LocalScope));
             Assert.Equal(1, MetadataTokens.GetRowNumber(localScope));
 
             var localVariable = builder.AddLocalVariable(default(LocalVariableAttributes), default(int), default(StringHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.LocalVariable]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.LocalVariable));
             Assert.Equal(1, MetadataTokens.GetRowNumber(localVariable));
 
             var localConstant = builder.AddLocalConstant(default(StringHandle), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.LocalConstant]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.LocalConstant));
             Assert.Equal(1, MetadataTokens.GetRowNumber(localConstant));
 
             var importScope = builder.AddImportScope(default(ImportScopeHandle), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.ImportScope]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.ImportScope));
             Assert.Equal(1, MetadataTokens.GetRowNumber(importScope));
 
             builder.AddStateMachineMethod(default(MethodDefinitionHandle), default(MethodDefinitionHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.StateMachineMethod]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.StateMachineMethod));
 
             builder.AddCustomDebugInformation(default(EntityHandle), default(GuidHandle), default(BlobHandle));
-            Assert.Equal(1, builder.GetRowCounts()[(int)TableIndex.CustomDebugInformation]);
+            Assert.Equal(1, builder.GetRowCount(TableIndex.CustomDebugInformation));
+
+            Assert.Equal(0, builder.GetRowCount(TableIndex.AssemblyOS));
+            Assert.Equal(0, builder.GetRowCount(TableIndex.AssemblyProcessor));
+            Assert.Equal(0, builder.GetRowCount(TableIndex.AssemblyRefOS));
+            Assert.Equal(0, builder.GetRowCount(TableIndex.AssemblyRefProcessor));
+            Assert.Equal(0, builder.GetRowCount(TableIndex.EventPtr));
+            Assert.Equal(0, builder.GetRowCount(TableIndex.FieldPtr));
+            Assert.Equal(0, builder.GetRowCount(TableIndex.MethodPtr));
+            Assert.Equal(0, builder.GetRowCount(TableIndex.ParamPtr));
+            Assert.Equal(0, builder.GetRowCount(TableIndex.PropertyPtr));
+
+            var rowCounts = builder.GetRowCounts();
+            Assert.Equal(MetadataTokens.TableCount, rowCounts.Length);
+            foreach (TableIndex tableIndex in Enum.GetValues(typeof(TableIndex)))
+            {
+                Assert.Equal(builder.GetRowCount(tableIndex), rowCounts[(int)tableIndex]);
+            }
+        }
+
+        [Fact]
+        public void GetRowCount_Errors()
+        {
+            var builder = new MetadataBuilder();
+            Assert.Throws<ArgumentOutOfRangeException>(() => builder.GetRowCount((TableIndex)0x2D));
+            Assert.Throws<ArgumentOutOfRangeException>(() => builder.GetRowCount((TableIndex)0x2E));
+            Assert.Throws<ArgumentOutOfRangeException>(() => builder.GetRowCount((TableIndex)0x2F));
+            Assert.Throws<ArgumentOutOfRangeException>(() => builder.GetRowCount((TableIndex)0x38));
+            Assert.Throws<ArgumentOutOfRangeException>(() => builder.GetRowCount((TableIndex)0x39));
+            Assert.Throws<ArgumentOutOfRangeException>(() => builder.GetRowCount((TableIndex)255));
         }
 
         /// <summary>
@@ -264,10 +293,10 @@ namespace System.Reflection.Metadata.Ecma335.Tests
         public void Heaps_Empty()
         {
             var mdBuilder = new MetadataBuilder();
-            mdBuilder.CompleteHeaps();
+            var serialized = mdBuilder.GetSerializedMetadata(MetadataRootBuilder.EmptyRowCounts, 12, isStandaloneDebugMetadata: false);
 
             var builder = new BlobBuilder();
-            mdBuilder.WriteHeapsTo(builder);
+            mdBuilder.WriteHeapsTo(builder, serialized.StringHeap);
 
             AssertEx.Equal(new byte[]
             {
@@ -310,19 +339,19 @@ namespace System.Reflection.Metadata.Ecma335.Tests
             var b1 = mdBuilder.GetOrAddBlob(new byte[] { 1, 2 });
             Assert.Equal(1, b1.GetHeapOffset());
 
-            mdBuilder.CompleteHeaps();
+            var serialized = mdBuilder.GetSerializedMetadata(MetadataRootBuilder.EmptyRowCounts, 12, isStandaloneDebugMetadata: false);
 
             Assert.Equal(0, mdBuilder.SerializeHandle(g0));
             Assert.Equal(1, mdBuilder.SerializeHandle(g1));
-            Assert.Equal(0, mdBuilder.SerializeHandle(s0));
-            Assert.Equal(1, mdBuilder.SerializeHandle(s1));
+            Assert.Equal(0, mdBuilder.SerializeHandle(serialized.StringMap, s0));
+            Assert.Equal(1, mdBuilder.SerializeHandle(serialized.StringMap, s1));
             Assert.Equal(1, mdBuilder.SerializeHandle(us0));
             Assert.Equal(3, mdBuilder.SerializeHandle(us1));
             Assert.Equal(0, mdBuilder.SerializeHandle(b0));
             Assert.Equal(1, mdBuilder.SerializeHandle(b1));
 
             var heaps = new BlobBuilder();
-            mdBuilder.WriteHeapsTo(heaps);
+            mdBuilder.WriteHeapsTo(heaps, serialized.StringHeap);
 
             AssertEx.Equal(new byte[] 
             {
@@ -374,18 +403,18 @@ namespace System.Reflection.Metadata.Ecma335.Tests
             var b1 = mdBuilder.GetOrAddBlob(new byte[] { 1, 2 });
             Assert.Equal(0x31, b1.GetHeapOffset());
 
-            mdBuilder.CompleteHeaps();
+            var serialized = mdBuilder.GetSerializedMetadata(MetadataRootBuilder.EmptyRowCounts, 12, isStandaloneDebugMetadata: false);
 
             Assert.Equal(5, mdBuilder.SerializeHandle(g));
-            Assert.Equal(0, mdBuilder.SerializeHandle(s0));
-            Assert.Equal(0x21, mdBuilder.SerializeHandle(s1));
+            Assert.Equal(0, mdBuilder.SerializeHandle(serialized.StringMap, s0));
+            Assert.Equal(0x21, mdBuilder.SerializeHandle(serialized.StringMap, s1));
             Assert.Equal(0x11, mdBuilder.SerializeHandle(us0));
             Assert.Equal(0x13, mdBuilder.SerializeHandle(us1));
             Assert.Equal(0, mdBuilder.SerializeHandle(b0));
             Assert.Equal(0x31, mdBuilder.SerializeHandle(b1));
 
             var heaps = new BlobBuilder();
-            mdBuilder.WriteHeapsTo(heaps);
+            mdBuilder.WriteHeapsTo(heaps, serialized.StringHeap);
 
             AssertEx.Equal(new byte[]
             {
@@ -421,10 +450,10 @@ namespace System.Reflection.Metadata.Ecma335.Tests
             Assert.Equal(MetadataTokens.GuidHandle(1), mdBuilder.ReserveGuid(out guidFixup));
             Assert.Equal(MetadataTokens.UserStringHandle(1), mdBuilder.ReserveUserString(3, out usFixup));
 
-            mdBuilder.CompleteHeaps();
+            var serialized = mdBuilder.GetSerializedMetadata(MetadataRootBuilder.EmptyRowCounts, 12, isStandaloneDebugMetadata: false);
 
             var builder = new BlobBuilder();
-            mdBuilder.WriteHeapsTo(builder);
+            mdBuilder.WriteHeapsTo(builder, serialized.StringHeap);
 
             AssertEx.Equal(new byte[]
             {
diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/MetadataRootBuilderTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/MetadataRootBuilderTests.cs
new file mode 100644 (file)
index 0000000..b71aea0
--- /dev/null
@@ -0,0 +1,387 @@
+// 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.Collections.Immutable;
+using System.Reflection.Metadata.Tests;
+using Xunit;
+
+namespace System.Reflection.Metadata.Ecma335.Tests
+{
+    public class MetadataRootBuilderTests
+    {
+        private string ReadVersion(BlobBuilder metadata)
+        {
+            using (var provider = MetadataReaderProvider.FromMetadataImage(metadata.ToImmutableArray()))
+            {
+                return provider.GetMetadataReader().MetadataVersion;
+            }
+        }
+
+        [Fact]
+        public void Ctor_Errors()
+        {
+            var mdBuilder = new MetadataBuilder();
+
+            Assert.Throws<ArgumentNullException>(() => new MetadataRootBuilder(null));
+            Assert.Throws<ArgumentException>(() => new MetadataRootBuilder(mdBuilder, new string('x', 255)));
+        }
+
+        [Fact]
+        public void Serialize_Errors()
+        {
+            var mdBuilder = new MetadataBuilder();
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
+            var builder = new BlobBuilder();
+
+            Assert.Throws<ArgumentNullException>(() => rootBuilder.Serialize(null, 0, 0));
+            Assert.Throws<ArgumentOutOfRangeException>(() => rootBuilder.Serialize(builder, -1, 0));
+            Assert.Throws<ArgumentOutOfRangeException>(() => rootBuilder.Serialize(builder, 0, -1));
+        }
+
+        [Fact]
+        public void Headers()
+        {
+            var mdBuilder = new MetadataBuilder();
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
+
+            var builder = new BlobBuilder();
+            rootBuilder.Serialize(builder, 0, 0);
+
+            AssertEx.Equal(new byte[]
+            {
+                // signature:
+                0x42, 0x53, 0x4A, 0x42,
+                // major version (1)
+                0x01, 0x00,
+                // minor version (1)
+                0x01, 0x00,
+                // reserved (0)
+                0x00, 0x00, 0x00, 0x00,
+
+                // padded version length:
+                0x0C, 0x00, 0x00, 0x00,
+
+                // padded version:
+                (byte)'v', (byte)'4', (byte)'.', (byte)'0', (byte)'.', (byte)'3', (byte)'0', (byte)'3', (byte)'1', (byte)'9', 0x00, 0x00,
+
+                // flags (0):
+                0x00, 0x00,
+
+                // stream count:
+                0x05, 0x00,
+
+                // stream headers:
+                0x6C, 0x00, 0x00, 0x00,
+                0x1C, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'~', 0x00, 0x00,
+
+                0x88, 0x00, 0x00, 0x00,
+                0x04, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'S', (byte)'t', (byte)'r', (byte)'i', (byte)'n', (byte)'g', (byte)'s', 0x00, 0x00, 0x00, 0x00,
+
+                0x8C, 0x00, 0x00, 0x00,
+                0x04, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'U', (byte)'S', 0x00,
+
+                0x90, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'G', (byte)'U', (byte)'I', (byte)'D', 0x00, 0x00, 0x00,
+
+                0x90, 0x00, 0x00, 0x00,
+                0x04, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'B', (byte)'l', (byte)'o', (byte)'b', 0x00, 0x00, 0x00,
+
+                // --------
+                // #~
+                // --------
+                
+                // Reserved (0)
+                0x00, 0x00, 0x00, 0x00,
+                
+                // Major Version (2)
+                0x02,
+
+                // Minor Version (0)
+                0x00,
+
+                // Heap Sizes
+                0x00,
+
+                // Reserved (1)
+                0x01,
+
+                // Present tables
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+                // Sorted tables
+                0x00, 0xFA, 0x01, 0x33, 0x00, 0x16, 0x00, 0x00, 
+
+                // Rows (empty)
+                // Tables (empty)
+
+                // Padding and alignment
+                0x00, 0x00, 0x00, 0x00,
+
+                // --------
+                // #Strings
+                // --------
+
+                0x00, 0x00, 0x00, 0x00,
+
+                // --------
+                // #US
+                // --------
+                0x00, 0x00, 0x00, 0x00,
+
+                // --------
+                // #GUID
+                // --------
+
+                // --------
+                // #Blob
+                // --------
+
+                0x00, 0x00, 0x00, 0x00,
+            }, builder.ToArray());
+        }
+
+        [Fact]
+        public void EncHeaders()
+        {
+            var mdBuilder = new MetadataBuilder();
+            mdBuilder.AddEncLogEntry(MetadataTokens.MethodDefinitionHandle(1), EditAndContinueOperation.AddMethod);
+
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
+
+            var builder = new BlobBuilder();
+            rootBuilder.Serialize(builder, 0, 0);
+
+            AssertEx.Equal(new byte[]
+            {
+                // signature:
+                0x42, 0x53, 0x4A, 0x42,
+                // major version (1)
+                0x01, 0x00,
+                // minor version (1)
+                0x01, 0x00,
+                // reserved (0)
+                0x00, 0x00, 0x00, 0x00,
+
+                // padded version length:
+                0x0C, 0x00, 0x00, 0x00,
+
+                // padded version:
+                (byte)'v', (byte)'4', (byte)'.', (byte)'0', (byte)'.', (byte)'3', (byte)'0', (byte)'3', (byte)'1', (byte)'9', 0x00, 0x00,
+
+                // flags (0):
+                0x00, 0x00,
+
+                // stream count:
+                0x06, 0x00,
+
+                // stream headers:
+                0x7C, 0x00, 0x00, 0x00,
+                0x28, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'-', 0x00, 0x00,
+
+                0xA4, 0x00, 0x00, 0x00,
+                0x04, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'S', (byte)'t', (byte)'r', (byte)'i', (byte)'n', (byte)'g', (byte)'s', 0x00, 0x00, 0x00, 0x00,
+
+                0xA8, 0x00, 0x00, 0x00,
+                0x04, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'U', (byte)'S', 0x00,
+
+                0xAC, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'G', (byte)'U', (byte)'I', (byte)'D', 0x00, 0x00, 0x00,
+
+                0xAC, 0x00, 0x00, 0x00,
+                0x04, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'B', (byte)'l', (byte)'o', (byte)'b', 0x00, 0x00, 0x00,
+
+                0xB0, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'J', (byte)'T', (byte)'D', 0x00, 0x00, 0x00, 0x00,
+
+                // --------
+                // #-
+                // --------
+                
+                // Reserved (0)
+                0x00, 0x00, 0x00, 0x00,
+                
+                // Major Version (2)
+                0x02,
+
+                // Minor Version (0)
+                0x00,
+
+                // Heap Sizes
+                0xA7,
+
+                // Reserved (1)
+                0x01,
+
+                // Present tables
+                0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+
+                // Sorted tables
+                0x00, 0xFA, 0x01, 0x33, 0x00, 0x16, 0x00, 0x00, 
+
+                // Rows
+                0x01, 0x00, 0x00, 0x00,
+
+                //
+                // EncLog Table (token, operation)
+                //
+
+                0x01, 0x00, 0x00, 0x06,
+                0x01, 0x00, 0x00, 0x00,
+
+                // Padding and alignment
+                0x00, 0x00, 0x00, 0x00,
+
+                // --------
+                // #Strings
+                // --------
+
+                0x00, 0x00, 0x00, 0x00,
+
+                // --------
+                // #US
+                // --------
+                0x00, 0x00, 0x00, 0x00,
+
+                // --------
+                // #GUID
+                // --------
+
+                // --------
+                // #Blob
+                // --------
+
+                0x00, 0x00, 0x00, 0x00,
+                
+                // --------
+                // #JTD
+                // --------
+            }, builder.ToArray());
+        }
+
+        [Fact]
+        public void MetadataVersion_Default()
+        {
+            var mdBuilder = new MetadataBuilder();
+            mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
+
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
+
+            var builder = new BlobBuilder();
+            rootBuilder.Serialize(builder, 0, 0);
+
+            AssertEx.Equal(new byte[] 
+            {
+                // padded version length:
+                0x0C, 0x00, 0x00, 0x00,
+
+                // padded version:
+                (byte)'v', (byte)'4', (byte)'.', (byte)'0', (byte)'.', (byte)'3', (byte)'0', (byte)'3', (byte)'1', (byte)'9', 0x00, 0x00,
+            }, builder.Slice(12, -132));
+
+            Assert.Equal(rootBuilder.MetadataVersion, ReadVersion(builder));
+        }
+
+        [Fact]
+        public void MetadataVersion_Empty()
+        {
+            var version = "";
+
+            var mdBuilder = new MetadataBuilder();
+            mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
+
+            var rootBuilder = new MetadataRootBuilder(mdBuilder, version);
+
+            var builder = new BlobBuilder();
+            rootBuilder.Serialize(builder, 0, 0);
+
+            AssertEx.Equal(new byte[]
+            {
+                // padded version length:
+                0x04, 0x00, 0x00, 0x00,
+
+                // padded version:
+                0x00, 0x00, 0x00, 0x00,
+            }, builder.Slice(12, -132));
+
+            Assert.Equal(version, ReadVersion(builder));
+        }
+
+        [Fact]
+        public void MetadataVersion_MaxLength()
+        {
+            var version = new string('x', 254);
+
+            var mdBuilder = new MetadataBuilder();
+            mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
+
+            var rootBuilder = new MetadataRootBuilder(mdBuilder, version);
+
+            var builder = new BlobBuilder();
+            rootBuilder.Serialize(builder, 0, 0);
+
+            AssertEx.Equal(new byte[]
+            {
+                // padded version length:
+                0x00, 0x01, 0x00, 0x00,
+
+                // padded version:
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+                0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x00, 0x00
+            }, builder.Slice(12, -132));
+
+            Assert.Equal(version, ReadVersion(builder));
+        }
+
+        [Fact]
+        public void MetadataVersion()
+        {
+            var version = "\u1234\ud800";
+
+            var mdBuilder = new MetadataBuilder();
+            mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
+
+            var rootBuilder = new MetadataRootBuilder(mdBuilder, version);
+
+            var builder = new BlobBuilder();
+            rootBuilder.Serialize(builder, 0, 0);
+
+            AssertEx.Equal(new byte[]
+            {
+                // padded version length:
+                0x08, 0x00, 0x00, 0x00,
+
+                // padded version:
+                0xE1, 0x88, 0xB4, 0xED, 0xA0, 0x80, 0x00, 0x00,
+            }, builder.Slice(12, -132));
+
+            // the default decoder replaces bad byte sequences by U+FFFD
+            Assert.Equal("\u1234\ufffd\ufffd", ReadVersion(builder));
+        }
+    }
+}
index 3f6e05d..6ed4966 100644 (file)
@@ -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 System.Linq;
+using System.Reflection.Metadata.Tests;
 using Xunit;
 
 namespace System.Reflection.Metadata.Ecma335.Tests
@@ -79,6 +81,102 @@ namespace System.Reflection.Metadata.Ecma335.Tests
         }
 
         [Fact]
+        public void TryGetTableIndex()
+        {
+            var kinds = 
+                from i in Enumerable.Range(0, 255)
+                let index = new Func<HandleKind, TableIndex?>(k => 
+                {
+                    TableIndex ti;
+                    if (MetadataTokens.TryGetTableIndex(k, out ti))
+                    {
+                        Assert.Equal((int)k, (int)ti);
+                        return ti;
+                    }
+
+                    return null;
+                })((HandleKind)i)
+                where index != null
+                select index.Value;
+
+            AssertEx.Equal(new TableIndex[] 
+            {
+                TableIndex.Module,
+                TableIndex.TypeRef,
+                TableIndex.TypeDef,
+                TableIndex.FieldPtr,
+                TableIndex.Field,
+                TableIndex.MethodPtr,
+                TableIndex.MethodDef,
+                TableIndex.ParamPtr,
+                TableIndex.Param,
+                TableIndex.InterfaceImpl,
+                TableIndex.MemberRef,
+                TableIndex.Constant,
+                TableIndex.CustomAttribute,
+                TableIndex.FieldMarshal,
+                TableIndex.DeclSecurity,
+                TableIndex.ClassLayout,
+                TableIndex.FieldLayout,
+                TableIndex.StandAloneSig,
+                TableIndex.EventMap,
+                TableIndex.EventPtr,
+                TableIndex.Event,
+                TableIndex.PropertyMap,
+                TableIndex.PropertyPtr,
+                TableIndex.Property,
+                TableIndex.MethodSemantics,
+                TableIndex.MethodImpl,
+                TableIndex.ModuleRef,
+                TableIndex.TypeSpec,
+                TableIndex.ImplMap,
+                TableIndex.FieldRva,
+                TableIndex.EncLog,
+                TableIndex.EncMap,
+                TableIndex.Assembly,
+                TableIndex.AssemblyRef,
+                TableIndex.File,
+                TableIndex.ExportedType,
+                TableIndex.ManifestResource,
+                TableIndex.NestedClass,
+                TableIndex.GenericParam,
+                TableIndex.MethodSpec,
+                TableIndex.GenericParamConstraint,
+                TableIndex.Document,
+                TableIndex.MethodDebugInformation,
+                TableIndex.LocalScope,
+                TableIndex.LocalVariable,
+                TableIndex.LocalConstant,
+                TableIndex.ImportScope,
+                TableIndex.StateMachineMethod,
+                TableIndex.CustomDebugInformation
+            }, kinds);
+        }
+
+        [Fact]
+        public void TryGetHeapIndex()
+        {
+            HeapIndex index;
+            Assert.True(MetadataTokens.TryGetHeapIndex(HandleKind.Blob, out index));
+            Assert.Equal(HeapIndex.Blob, index);
+
+            Assert.True(MetadataTokens.TryGetHeapIndex(HandleKind.String, out index));
+            Assert.Equal(HeapIndex.String, index);
+
+            Assert.True(MetadataTokens.TryGetHeapIndex(HandleKind.UserString, out index));
+            Assert.Equal(HeapIndex.UserString, index);
+
+            Assert.True(MetadataTokens.TryGetHeapIndex(HandleKind.NamespaceDefinition, out index));
+            Assert.Equal(HeapIndex.String, index);
+
+            Assert.True(MetadataTokens.TryGetHeapIndex(HandleKind.Guid, out index));
+            Assert.Equal(HeapIndex.Guid, index);
+
+            Assert.False(MetadataTokens.TryGetHeapIndex(HandleKind.Constant, out index));
+            Assert.False(MetadataTokens.TryGetHeapIndex((HandleKind)255, out index));
+        }
+
+        [Fact]
         public void HandleFactories_FromToken()
         {
             Assert.Equal(s_assemblyRefHandle, MetadataTokens.Handle(0x23000001));
diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/PortablePdbBuilderTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/PortablePdbBuilderTests.cs
new file mode 100644 (file)
index 0000000..34a7def
--- /dev/null
@@ -0,0 +1,225 @@
+// 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.Collections.Immutable;
+using System.Reflection.Metadata.Tests;
+using Xunit;
+
+namespace System.Reflection.Metadata.Ecma335.Tests
+{
+    public class PortablePdbBuilderTests
+    {
+        [Fact]
+        public void Ctor_Errors()
+        {
+            var mdBuilder = new MetadataBuilder();
+
+            Assert.Throws<ArgumentNullException>(() => new PortablePdbBuilder(null, MetadataRootBuilder.EmptyRowCounts, default(MethodDefinitionHandle)));
+            Assert.Throws<ArgumentNullException>(() => new PortablePdbBuilder(mdBuilder, default(ImmutableArray<int>), default(MethodDefinitionHandle)));
+
+            var rowCounts = new int[128];
+            rowCounts[64] = 1;
+            Assert.Throws<ArgumentException>(() => new PortablePdbBuilder(mdBuilder, ImmutableArray.Create(rowCounts), default(MethodDefinitionHandle)));
+
+            rowCounts = new int[64];
+            rowCounts[63] = 1;
+            Assert.Throws<ArgumentException>(() => new PortablePdbBuilder(mdBuilder, ImmutableArray.Create(rowCounts), default(MethodDefinitionHandle)));
+
+            rowCounts = new int[64];
+            rowCounts[(int)TableIndex.EventPtr] = 1;
+            Assert.Throws<ArgumentException>(() => new PortablePdbBuilder(mdBuilder, ImmutableArray.Create(rowCounts), default(MethodDefinitionHandle)));
+
+            rowCounts = new int[64];
+            rowCounts[(int)TableIndex.CustomDebugInformation] = 1;
+            Assert.Throws<ArgumentException>(() => new PortablePdbBuilder(mdBuilder, ImmutableArray.Create(rowCounts), default(MethodDefinitionHandle)));
+
+            rowCounts = new int[64];
+            rowCounts[(int)TableIndex.MethodDef] = -1;
+            Assert.Throws<ArgumentOutOfRangeException>(() => new PortablePdbBuilder(mdBuilder, ImmutableArray.Create(rowCounts), default(MethodDefinitionHandle)));
+
+            rowCounts = new int[64];
+            rowCounts[(int)TableIndex.GenericParamConstraint] = 0x01000000;
+            Assert.Throws<ArgumentOutOfRangeException>(() => new PortablePdbBuilder(mdBuilder, ImmutableArray.Create(rowCounts), default(MethodDefinitionHandle)));
+        }
+
+        [Fact]
+        public void Serialize_Errors()
+        {
+            var mdBuilder = new MetadataBuilder();
+            var pdbBuilder = new PortablePdbBuilder(mdBuilder, MetadataRootBuilder.EmptyRowCounts, default(MethodDefinitionHandle));
+            var builder = new BlobBuilder();
+
+            Assert.Throws<ArgumentNullException>(() => pdbBuilder.Serialize(null));
+        }
+
+        [Fact]
+        public void Headers()
+        {
+            var mdBuilder = new MetadataBuilder();
+            var pdbBuilder = new PortablePdbBuilder(
+                mdBuilder,
+                MetadataRootBuilder.EmptyRowCounts,
+                MetadataTokens.MethodDefinitionHandle(0x123456),
+                _ => new BlobContentId(new Guid("44332211-6655-8877-AA99-010203040506"), 0xFFEEDDCC));
+
+            var builder = new BlobBuilder();
+            pdbBuilder.Serialize(builder);
+
+            AssertEx.Equal(new byte[]
+            {
+                // signature:
+                0x42, 0x53, 0x4A, 0x42,
+                // major version (1)
+                0x01, 0x00,
+                // minor version (1)
+                0x01, 0x00,
+                // reserved (0)
+                0x00, 0x00, 0x00, 0x00,
+
+                // padded version length:
+                0x0C, 0x00, 0x00, 0x00,
+
+                // padded version:
+                (byte)'P', (byte)'D', (byte)'B', (byte)' ', (byte)'v', (byte)'1', (byte)'.', (byte)'0', 0x00, 0x00, 0x00, 0x00,
+                
+                // flags (0):
+                0x00, 0x00,
+
+                // stream count:
+                0x06, 0x00,
+
+                // stream headers (offset, size, padded name)
+                0x7C, 0x00, 0x00, 0x00,
+                0x20, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'P', (byte)'d', (byte)'b', 0x00, 0x00, 0x00, 0x00,
+
+                0x9C, 0x00, 0x00, 0x00,
+                0x1C, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'~', 0x00, 0x00,
+
+                0xB8, 0x00, 0x00, 0x00,
+                0x04, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'S', (byte)'t', (byte)'r', (byte)'i', (byte)'n', (byte)'g', (byte)'s', 0x00, 0x00, 0x00, 0x00,
+
+                0xBC, 0x00, 0x00, 0x00,
+                0x04, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'U', (byte)'S', 0x00,
+
+                0xC0, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'G', (byte)'U', (byte)'I', (byte)'D', 0x00, 0x00, 0x00,
+
+                0xC0, 0x00, 0x00, 0x00,
+                0x04, 0x00, 0x00, 0x00,
+                (byte)'#', (byte)'B', (byte)'l', (byte)'o', (byte)'b', 0x00, 0x00, 0x00,
+
+                // --------
+                // #Pdb
+                // --------
+
+                // PDB ID
+                0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xAA, 0x99, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+                0xCC, 0xDD, 0xEE, 0xFF,
+
+                // EntryPoint
+                0x56, 0x34, 0x12, 0x06,
+
+                // ReferencedTypeSystemTables
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+                // TypeSystemTableRows (empty)
+
+                // --------
+                // #~
+                // --------
+                
+                // Reserved (0)
+                0x00, 0x00, 0x00, 0x00,
+
+                // Major Version (2)
+                0x02,
+
+                // Minor Version (0)
+                0x00,
+
+                // Heap Sizes
+                0x00,
+
+                // Reserved (1)
+                0x01,
+
+                // Present tables
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+                // Sorted tables
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+                // Rows (empty)
+                // Tables (empty)
+
+                // Padding and alignment
+                0x00, 0x00, 0x00, 0x00,
+                
+                // --------
+                // #Strings
+                // --------
+
+                0x00, 0x00, 0x00, 0x00,
+
+                // --------
+                // #US
+                // --------
+
+                0x00, 0x00, 0x00, 0x00,
+
+                // --------
+                // #GUID
+                // --------
+
+                // --------
+                // #Blob
+                // --------
+
+                0x00, 0x00, 0x00, 0x00,
+
+            }, builder.ToArray());
+        }
+
+        [Fact]
+        public void PdbStream_TypeSystemRowCounts()
+        {
+            var rowCounts = new int[MetadataTokens.TableCount];
+            rowCounts[(int)TableIndex.MethodDef] = 0xFFFFFF;
+            rowCounts[(int)TableIndex.TypeDef] = 0x123456;
+
+            var mdBuilder = new MetadataBuilder();
+            var pdbBuilder = new PortablePdbBuilder(
+                mdBuilder,
+                ImmutableArray.Create(rowCounts),
+                MetadataTokens.MethodDefinitionHandle(0x123456),
+                _ => new BlobContentId(new Guid("44332211-6655-8877-AA99-010203040506"), 0xFFEEDDCC));
+
+            var builder = new BlobBuilder();
+            pdbBuilder.Serialize(builder);
+
+            AssertEx.Equal(new byte[]
+            {
+                // PDB ID
+                0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xAA, 0x99, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+                0xCC, 0xDD, 0xEE, 0xFF,
+
+                // EntryPoint
+                0x56, 0x34, 0x12, 0x06,
+
+                // ReferencedTypeSystemTables
+                0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+                // TypeSystemTableRows
+                0x56, 0x34, 0x12, 0x00,
+                0xFF, 0xFF, 0xFF, 0x00
+
+            }, builder.Slice(124, -40));
+        }
+    }
+}
index aca8fb1..dcec177 100644 (file)
@@ -17,9 +17,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -85,9 +85,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -153,9 +153,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -221,9 +221,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -291,9 +291,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -363,9 +363,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -435,9 +435,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -505,9 +505,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -577,9 +577,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -649,9 +649,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -719,9 +719,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -791,9 +791,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -861,9 +861,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -931,9 +931,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1001,9 +1001,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1071,9 +1071,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1141,9 +1141,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1211,9 +1211,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1281,9 +1281,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1353,9 +1353,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1425,9 +1425,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1497,9 +1497,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1567,9 +1567,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1637,9 +1637,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1707,9 +1707,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1777,9 +1777,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1847,9 +1847,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1917,9 +1917,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -1987,9 +1987,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -2057,9 +2057,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -2127,9 +2127,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
@@ -2214,9 +2214,9 @@ namespace System.Reflection.Metadata.Tests
 
             mdBuilder.AddModule(0, default(StringHandle), default(GuidHandle), default(GuidHandle), default(GuidHandle));
 
-            var mdSerializer = new TypeSystemMetadataSerializer(mdBuilder, "v4.0.30319", isMinimalDelta: false);
+            var rootBuilder = new MetadataRootBuilder(mdBuilder);
             var mdBlob = new BlobBuilder();
-            mdSerializer.SerializeMetadata(mdBlob, 0, 0);
+            rootBuilder.Serialize(mdBlob, 0, 0);
 
             // validate sizes table rows that reference guids:
             using (var mdProvider = MetadataReaderProvider.FromMetadataImage(mdBlob.ToImmutableArray()))
index 66bdf96..a20e901 100644 (file)
@@ -61,7 +61,7 @@ namespace System.Reflection.PortableExecutable.Tests
         {
             var peBuilder = new ManagedPEBuilder(
                 entryPointHandle.IsNil ? PEHeaderBuilder.CreateLibraryHeader() : PEHeaderBuilder.CreateExecutableHeader(),
-                new TypeSystemMetadataSerializer(metadataBuilder, "v4.0.30319", isMinimalDelta: false),
+                new MetadataRootBuilder(metadataBuilder),
                 ilBuilder,
                 entryPoint: entryPointHandle,
                 flags: CorFlags.ILOnly | (privateKeyOpt != null ? CorFlags.StrongNameSigned : 0),
@@ -91,7 +91,7 @@ namespace System.Reflection.PortableExecutable.Tests
         public void ManagedPEBuilder_Errors()
         {
             var hdr = new PEHeaderBuilder();
-            var ms = new TypeSystemMetadataSerializer(new MetadataBuilder(), "v4.0.30319", false);
+            var ms = new MetadataRootBuilder(new MetadataBuilder());
             var il = new BlobBuilder();
 
             Assert.Throws<ArgumentNullException>(() => new ManagedPEBuilder(null, ms, il));
@@ -486,7 +486,7 @@ namespace System.Reflection.PortableExecutable.Tests
             
             var peBuilder = new ManagedPEBuilder(
                 PEHeaderBuilder.CreateLibraryHeader(),
-                new TypeSystemMetadataSerializer(metadataBuilder, "v4.0.30319", false),
+                new MetadataRootBuilder(metadataBuilder),
                 ilBuilder,
                 nativeResources: new TestResourceSectionBuilder(),
                 deterministicIdProvider: content => s_contentId);
@@ -531,7 +531,7 @@ namespace System.Reflection.PortableExecutable.Tests
 
             var peBuilder = new ManagedPEBuilder(
                 PEHeaderBuilder.CreateLibraryHeader(),
-                new TypeSystemMetadataSerializer(metadataBuilder, "v4.0.30319", false),
+                new MetadataRootBuilder(metadataBuilder),
                 ilBuilder,
                 nativeResources: new BadResourceSectionBuilder(),
                 deterministicIdProvider: content => s_contentId);
index 8484f63..354391c 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
   <PropertyGroup>
@@ -36,6 +36,8 @@
     <Compile Include="Metadata\BlobTests.cs" />
     <Compile Include="Metadata\BlobUtilitiesTests.cs" />
     <Compile Include="Metadata\Ecma335\CodedIndexTests.cs" />
+    <Compile Include="Metadata\Ecma335\MetadataRootBuilderTests.cs" />
+    <Compile Include="Metadata\Ecma335\PortablePdbBuilderTests.cs" />
     <Compile Include="Metadata\LargeTablesAndHeapsTests.cs" />
     <Compile Include="Metadata\TypeSystem\ClassLayoutRow.cs" />
     <Compile Include="Metadata\Decoding\CustomAttributeDecoderTests.cs" />
@@ -59,6 +61,7 @@
     <Compile Include="Metadata\PortablePdb\DocumentNameTests.cs" />
     <Compile Include="PortableExecutable\DebugDirectoryBuilderTests.cs" />
     <Compile Include="PortableExecutable\PEMemoryBlockTests.cs" />
+    <Compile Include="TestUtilities\ByteArrayUtilities.cs" />
     <Compile Include="TestUtilities\PinnedBlob.cs" />
     <Compile Include="TestUtilities\SigningUtilities.cs" />
     <Compile Include="Utilities\AbstractMemoryBlockTests.cs" />
index d4fd23f..dbda99e 100644 (file)
@@ -404,7 +404,7 @@ namespace System.Reflection.Metadata.Tests
         {
             if (itemInspector == null)
             {
-                if (expected is IEnumerable<byte>)
+                if (typeof(T) == typeof(byte))
                 {
                     itemInspector = b => $"0x{b:X2}";
                 }
diff --git a/src/libraries/System.Reflection.Metadata/tests/TestUtilities/ByteArrayUtilities.cs b/src/libraries/System.Reflection.Metadata/tests/TestUtilities/ByteArrayUtilities.cs
new file mode 100644 (file)
index 0000000..d3ae6ab
--- /dev/null
@@ -0,0 +1,26 @@
+// 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.
+
+namespace System.Reflection.Metadata.Tests
+{
+    public static class ByteArrayUtilities
+    {
+        public static byte[] Slice(this BlobBuilder bytes, int start, int end)
+        {
+            return Slice(bytes.ToArray(), start, end);
+        }
+
+        public static byte[] Slice(this byte[] bytes, int start, int end)
+        {
+            if (end < 0)
+            {
+                end = bytes.Length + end;
+            }
+
+            var result = new byte[end - start];
+            Array.Copy(bytes, start, result, 0, result.Length);
+            return result;
+        }
+    }
+}