Repository navigation
Fix PDB document identity to include all document row fields #580
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,6 +75,9 @@ public PdbDocument(SymbolDocument symDoc) : this(symDoc, partial: false) { | |
| if (symDoc is null) | ||
| throw new ArgumentNullException(nameof(symDoc)); | ||
| Url = symDoc.URL; | ||
| Language = symDoc.Language; | ||
| CheckSumAlgorithmId = symDoc.CheckSumAlgorithmId; | ||
| CheckSum = symDoc.CheckSum; | ||
| if (!partial) | ||
| Initialize(symDoc); | ||
| } | ||
|
|
@@ -83,11 +86,8 @@ internal static PdbDocument CreatePartialForCompare(SymbolDocument symDoc) => | |
| new PdbDocument(symDoc, partial: true); | ||
|
|
||
| internal void Initialize(SymbolDocument symDoc) { | ||
| Language = symDoc.Language; | ||
| LanguageVendor = symDoc.LanguageVendor; | ||
| DocumentType = symDoc.DocumentType; | ||
| CheckSumAlgorithmId = symDoc.CheckSumAlgorithmId; | ||
| CheckSum = symDoc.CheckSum; | ||
| customDebugInfos = new List<PdbCustomDebugInfo>(); | ||
| foreach (var cdi in symDoc.CustomDebugInfos) | ||
| customDebugInfos.Add(cdi); | ||
|
|
@@ -113,14 +113,25 @@ public PdbDocument(string url, Guid language, Guid languageVendor, Guid document | |
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Url ?? string.Empty); | ||
| public override int GetHashCode() { | ||
| unchecked { | ||
| int hash = StringComparer.Ordinal.GetHashCode(Url ?? string.Empty); | ||
| hash += CheckSumAlgorithmId.GetHashCode(); | ||
| hash += Utils.GetHashCode(CheckSum); | ||
| hash += Language.GetHashCode(); | ||
| return hash; | ||
| } | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public override bool Equals(object obj) { | ||
| var other = obj as PdbDocument; | ||
| if (other is null) | ||
| return false; | ||
| return StringComparer.OrdinalIgnoreCase.Equals(Url ?? string.Empty, other.Url ?? string.Empty); | ||
| return StringComparer.Ordinal.Equals(Url ?? string.Empty, other.Url ?? string.Empty) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line, and the one in To check it, I compiled a C# library with two A VB library using I didn't test two things at runtime:
Whichever URL comparison you choose, a test covering names that differ only by case would help. |
||
| && CheckSumAlgorithmId == other.CheckSumAlgorithmId | ||
| && Utils.Equals(CheckSum, other.CheckSum) | ||
| && Language == other.Language; | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CheckSum,CheckSumAlgorithmIdandLanguageall have public setters, andPdbStatekeeps loaded documents in aDictionary<PdbDocument, PdbDocument>(docDict). When a method body is read later,PdbState.Add_NoLock(SymbolDocument)looks up each sequence point's document in that dictionary again. If a caller has edited a field that is part of the hash before that method body is read, the lookup misses. dnlib then creates a second copy of the document, andtokenToDocument.Addthrows. Today this happens only forUrl, which is #566: "Callingmodule.Write()after updating PDB document URLs causes aSystem.ArgumentException." This PR exposes the other three fields to the same problem; I reproduced it forCheckSumandLanguage.Here is the edit-and-write step from my repro.
pathis a compiled assembly with its compiler-written Portable PDB beside it, andoutPathis the output assembly. The only edit is the checksum of the document whose URL ends in/Calc.cs:At master, this writes the new checksum. With this PR it throws:
Editing
Languageinstead gives the same exception, and with no edit the write succeeds. After the checksum edit,PdbState.Remove(doc)also returnsfalseand leaves the document in the list; at master it returnstrue.A suggestion: a document read from a Portable PDB already has an identity, its Document row. If
Add_NoLock(SymbolDocument)looks up the token first, the lazy lookup no longer depends on fields the caller can change:I applied this on top of this PR. In my repro, the checksum, language and
Urledits then all write successfully, and the #579 checks above still pass. So it also covers the in-placeUrledit from #566; I didn't try #566'sRemoveAllDocumentsvariant.It doesn't fix
RemoveorGetExistingafter an edit, because those still hash the current values. So it may still be worth a sentence in the XML docs saying these fields shouldn't change while the document is in aPdbState.