@@ -339,9 +339,12 @@ let scopeSorter (scope1: PdbMethodScope) (scope2: PdbMethodScope) =
339339type PortablePdbGenerator
340340 ( embedAllSource: bool, embedSourceList: string list, sourceLink: string, checksumAlgorithm, info: PdbData, pathMap: PathMap) =
341341
342- let docs = info.Documents
342+ // Deterministic: build the Document table in a stable order by mapped file path,
343+ // but preserve the original-document-index -> handle mapping by filename.
344+ let originalDocFiles = info.Documents |> Array.map ( fun d -> d.File)
343345
344- // The metadata to wite to the PortablePDB (Roslyn = _debugMetadataOpt)
346+ let docsSorted =
347+ info.Documents |> Array.sortBy ( fun d -> PathMap.apply pathMap d.File)
345348
346349 let metadata = MetadataBuilder()
347350
@@ -414,15 +417,16 @@ type PortablePdbGenerator
414417
415418 Some( builder.ToImmutableArray())
416419
420+ // Build Document table in deterministic order
417421 let documentIndex =
418- let mutable index = Dictionary< string, DocumentHandle>( docs .Length)
422+ let mutable index = Dictionary< string, DocumentHandle>( docsSorted .Length)
419423
420- let docLength = docs.Length + if String.IsNullOrEmpty sourceLink then 1 else 0
424+ let docLength =
425+ docsSorted.Length + ( if String.IsNullOrWhiteSpace sourceLink then 0 else 1 )
421426
422427 metadata.SetCapacity( TableIndex.Document, docLength)
423428
424- for doc in docs do
425- // For F# Interactive, file name 'stdin' gets generated for interactive inputs
429+ for doc in docsSorted do
426430 let handle =
427431 match checkSum doc.File checksumAlgorithm with
428432 | Some( hashAlg, checkSum) ->
@@ -472,11 +476,12 @@ type PortablePdbGenerator
472476
473477 let mutable lastLocalVariableHandle = Unchecked.defaultof< LocalVariableHandle>
474478
479+ // IMPORTANT: map original document index -> filename -> handle
475480 let getDocumentHandle d =
476- if docs. Length = 0 || d < 0 || d > docs .Length then
481+ if info.Documents. Length = 0 || d < 0 || d >= info.Documents .Length then
477482 Unchecked.defaultof< DocumentHandle>
478483 else
479- match documentIndex.TryGetValue( docs [ d]. File ) with
484+ match documentIndex.TryGetValue( originalDocFiles [ d]) with
480485 | false , _ -> Unchecked.defaultof< DocumentHandle>
481486 | true , h -> h
482487
@@ -559,7 +564,16 @@ type PortablePdbGenerator
559564 let serializeImportsBlob ( imports : PdbImport []) =
560565 let writer = BlobBuilder()
561566
562- for import in imports do
567+ let importsSorted =
568+ imports
569+ |> Array.sortWith ( fun a b ->
570+ match a, b with
571+ | ImportType t1, ImportType t2 -> compare t1 t2
572+ | ImportNamespace n1, ImportNamespace n2 -> compare n1 n2
573+ | ImportType _, ImportNamespace _ -> - 1
574+ | ImportNamespace _, ImportType _ -> 1 )
575+
576+ for import in importsSorted do
563577 serializeImport writer import
564578
565579 metadata.GetOrAddBlob( writer)
@@ -636,7 +650,8 @@ type PortablePdbGenerator
636650 )
637651 |> ignore
638652
639- for localVariable in scope.Locals do
653+ // Deterministic: write locals by stable index
654+ for localVariable in scope.Locals |> Array.sortBy ( fun l -> l.Index) do
640655 lastLocalVariableHandle <-
641656 metadata.AddLocalVariable(
642657 LocalVariableAttributes.None,
@@ -649,7 +664,7 @@ type PortablePdbGenerator
649664 let sps =
650665 match minfo.DebugRange with
651666 | None -> Array.empty
652- | Some _ -> minfo.DebugPoints
667+ | Some _ -> minfo.DebugPoints |> Array.sortWith SequencePoint.orderByOffset
653668
654669 let builder = BlobBuilder()
655670 builder.WriteCompressedInteger( minfo.LocalSignatureToken)
0 commit comments