@@ -343,9 +343,13 @@ let scopeSorter (scope1: PdbMethodScope) (scope2: PdbMethodScope) =
343343type PortablePdbGenerator
344344 ( embedAllSource: bool, embedSourceList: string list, sourceLink: string, checksumAlgorithm, info: PdbData, pathMap: PathMap) =
345345
346- let docs = info.Documents
346+ // Deterministic: build the Document table in a stable order by mapped file path,
347+ // but preserve the original-document-index -> handle mapping by filename.
348+ let originalDocFiles = info.Documents |> Array.map ( fun d -> d.File)
347349
348- // The metadata to wite to the PortablePDB (Roslyn = _debugMetadataOpt)
350+ let docsSorted =
351+ info.Documents
352+ |> Array.sortBy ( fun d -> PathMap.apply pathMap d.File)
349353
350354 let metadata = MetadataBuilder()
351355
@@ -418,15 +422,13 @@ type PortablePdbGenerator
418422
419423 Some( builder.ToImmutableArray())
420424
425+ // Build Document table in deterministic order
421426 let documentIndex =
422- let mutable index = Dictionary< string, DocumentHandle>( docs.Length)
423-
424- let docLength = docs.Length + if String.IsNullOrEmpty sourceLink then 1 else 0
425-
427+ let mutable index = Dictionary< string, DocumentHandle>( docsSorted.Length)
428+ let docLength = docsSorted.Length + ( if String.IsNullOrWhiteSpace sourceLink then 0 else 1 )
426429 metadata.SetCapacity( TableIndex.Document, docLength)
427430
428- for doc in docs do
429- // For F# Interactive, file name 'stdin' gets generated for interactive inputs
431+ for doc in docsSorted do
430432 let handle =
431433 match checkSum doc.File checksumAlgorithm with
432434 | Some( hashAlg, checkSum) ->
@@ -476,11 +478,12 @@ type PortablePdbGenerator
476478
477479 let mutable lastLocalVariableHandle = Unchecked.defaultof< LocalVariableHandle>
478480
481+ // IMPORTANT: map original document index -> filename -> handle
479482 let getDocumentHandle d =
480- if docs. Length = 0 || d < 0 || d > docs .Length then
483+ if info.Documents. Length = 0 || d < 0 || d >= info.Documents .Length then
481484 Unchecked.defaultof< DocumentHandle>
482485 else
483- match documentIndex.TryGetValue( docs [ d]. File ) with
486+ match documentIndex.TryGetValue( originalDocFiles [ d]) with
484487 | false , _ -> Unchecked.defaultof< DocumentHandle>
485488 | true , h -> h
486489
@@ -563,7 +566,16 @@ type PortablePdbGenerator
563566 let serializeImportsBlob ( imports : PdbImport []) =
564567 let writer = new BlobBuilder()
565568
566- for import in imports do
569+ let importsSorted =
570+ imports
571+ |> Array.sortWith ( fun a b ->
572+ match a, b with
573+ | ImportType t1, ImportType t2 -> compare t1 t2
574+ | ImportNamespace n1, ImportNamespace n2 -> compare n1 n2
575+ | ImportType _, ImportNamespace _ -> - 1
576+ | ImportNamespace _, ImportType _ -> 1 )
577+
578+ for import in importsSorted do
567579 serializeImport writer import
568580
569581 metadata.GetOrAddBlob( writer)
@@ -640,7 +652,8 @@ type PortablePdbGenerator
640652 )
641653 |> ignore
642654
643- for localVariable in scope.Locals do
655+ // Deterministic: write locals by stable index
656+ for localVariable in scope.Locals |> Array.sortBy ( fun l -> l.Index) do
644657 lastLocalVariableHandle <-
645658 metadata.AddLocalVariable(
646659 LocalVariableAttributes.None,
@@ -653,7 +666,7 @@ type PortablePdbGenerator
653666 let sps =
654667 match minfo.DebugRange with
655668 | None -> Array.empty
656- | Some _ -> minfo.DebugPoints
669+ | Some _ -> minfo.DebugPoints |> Array.sortWith SequencePoint.orderByOffset
657670
658671 let builder = BlobBuilder()
659672 builder.WriteCompressedInteger( minfo.LocalSignatureToken)
0 commit comments