Skip to content

Write compilation references and compilation options to PDBs - #14981

Draft
baronfel wants to merge 6 commits into
dotnet:mainfrom
baronfel:compilation-options-and-reference
Draft

baronfel wants to merge 6 commits into
dotnet:mainfrom
baronfel:compilation-options-and-reference

Conversation

@baronfel

Copy link
Copy Markdown
Member

Fixes #12002

This writes the Compilation Metadata References and Compilation Options tables to the CustomDebugInformation area of the PDB. There are a number of TODOs, and I'm not 100% sure I'm getting the actual information we will want to get a real end-to-end setup.

With these changes, we look like other .NET assemblies!

image

What this should do immediately is allow VS-based tooling to synthesize MetadataReferences from this cached data, so that go-to-def for sourcelinked files has a matching set of assembly references to do typechecking against (and therefore light up IDE tooling).

There are a number of TODOs that I'd like to get some feedback on. I'm fairly sure I'm reading required data correctly, but I want to make sure the sources I'm pulling it from are correct.
Also I'm certain I'm missing a number of compiler options that would be necessary to have on the read-side of this to ensure 1:1 compilation.
Finally, I haven't done the step that would map from the compilation options + compilation references to a proper FSharpProjectOptions - that would probably need to be done to show a true end-to-end in an editor.

I would love any and all review/pointers for the next steps here.

@vzarytovskii

vzarytovskii commented Mar 27, 2023 •

Copy link
Copy Markdown
Member

Test framework will probably also need to be extended, to check those:

let verifyPdb (options: PdbVerificationOption list) (result: CompilationResult) : CompilationResult =
match result with
| CompilationResult.Success r -> verifyPortablePdb r options
| _ -> failwith "Result should be \"Success\" in order to verify PDB."
result

Example for sequence points:

let private verifySequencePoints (reader: MetadataReader) expectedSequencePoints =
let sequencePoints =
[ for sp in reader.MethodDebugInformation do
let mdi = reader.GetMethodDebugInformation sp
yield! mdi.GetSequencePoints() ]
|> List.sortBy (fun sp -> sp.StartLine)
|> List.map (fun sp -> (Line sp.StartLine, Col sp.StartColumn, Line sp.EndLine, Col sp.EndColumn) )
if sequencePoints <> expectedSequencePoints then
failwith $"Expected sequence points are different from PDB.\nExpected: %A{expectedSequencePoints}\nActual: %A{sequencePoints}"

And import tables:

let private verifyPdbImportTables (reader: MetadataReader) (scopes: ImportScope list list) =
// There always should be 2 import scopes - 1 empty "root" one, and one flattened table of imports for current scope.
if reader.ImportScopes.Count < 2 then
failwith $"Expected to have at least 2 import scopes, but found {reader.ImportScopes.Count}."
// Sanity check: explicitly test that first import scope is indeed an apty one (i.e. there are no imports).
let rootScope = reader.ImportScopes.ToImmutableArray().Item(0) |> reader.GetImportScope
let rootScopeImportsLength = rootScope.GetImports().ToImmutableArray().Length
if rootScopeImportsLength <> 0 then
failwith $"Expected root scope to have 0 imports, but got {rootScopeImportsLength}."
let pdbScopes = [ for import in reader.ImportScopes -> reader.GetImportScope import ] |> List.skip 1 |> List.rev
if pdbScopes.Length <> scopes.Length then
failwith $"Expected import scopes amount is {scopes.Length}, but got {pdbScopes.Length}."
for (pdbScope, expectedScope) in List.zip pdbScopes scopes do
let imports = [ for import in pdbScope.GetImports() ->
match import.Kind with
| ImportDefinitionKind.ImportNamespace ->
let targetNamespaceBlob = import.TargetNamespace
let targetNamespaceBytes = reader.GetBlobBytes(targetNamespaceBlob)
let name = Encoding.UTF8.GetString(targetNamespaceBytes, 0, targetNamespaceBytes.Length)
Some { Kind = import.Kind; Name = name }
| _ -> None ] |> List.filter Option.isSome |> List.map Option.get
if expectedScope.Length <> imports.Length then
failwith $"Expected imports amount is {expectedScope.Length}, but got {imports.Length}\nExpected:\n%A{expectedScope}\nActual:%A{imports}"
if expectedScope <> imports then
failwith $"Expected imports are different from PDB.\nExpected:\n%A{expectedScope}\nActual:%A{imports}"

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

Consider emitting CompilationOptions into Portable PDB

2 participants