-
Notifications
You must be signed in to change notification settings - Fork 871
Add Roslyn-format EnC CustomDebugInformation codec and portable PDB method CDI emission #20018
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
Open
NatElkins
wants to merge
5
commits into
dotnet:main
Choose a base branch
from
NatElkins:pdb-method-cdi-emission
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
2cdfebb
Add Roslyn-format EnC CustomDebugInformation codec and portable PDB m…
NatElkins 3784dd5
Use ProcessStartInfo.Arguments for net472 compatibility
NatElkins 5ce25dc
Make the EnC CDI test module public for xunit v3 discovery
NatElkins b6de457
Fix EnC CDI cross-validation test process launch on Desktop and bare …
NatElkins d039cd5
Handle metadata-only methods in PDB ambiguity checks
NatElkins File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| /// Edit-and-Continue method debug information blobs. | ||
| /// | ||
| /// This module replicates, byte for byte, the three Portable-PDB CustomDebugInformation | ||
| /// blob formats Roslyn persists per method to support Edit and Continue | ||
| /// (roslyn/src/Compilers/Core/Portable/Emit/EditAndContinueMethodDebugInformation.cs): | ||
| /// | ||
| /// - EnC Local Slot Map (kind 755F52A8-91C5-45BE-B4B8-209571E552BD) | ||
| /// - EnC Lambda and Closure Map (kind A643004C-0240-496F-A783-30D64F4979DE) | ||
| /// - EnC State Machine State Map (kind 8B78CD68-2EDE-420B-980B-E15884B8AAA3) | ||
| /// | ||
| /// (GUIDs: roslyn/src/Dependencies/CodeAnalysis.Debugging/PortableCustomDebugInfoKinds.cs.) | ||
| /// | ||
| /// All multi-byte integers use the ECMA-335 compressed unsigned/signed encodings via | ||
| /// System.Reflection.Metadata's BlobBuilder.WriteCompressedInteger / | ||
| /// WriteCompressedSignedInteger and BlobReader.ReadCompressedInteger / | ||
| /// ReadCompressedSignedInteger, exactly as Roslyn writes/reads them. | ||
| /// | ||
| /// Every "syntax offset" slot in these blobs is an opaque, caller-defined integer key | ||
| /// (Roslyn: the syntax offset of the lambda/closure/state-machine-suspension syntax | ||
| /// node). This module does not require the key to be a source offset; it only requires | ||
| /// determinism across generations. tryEncodeOccurrenceKey/decodeOccurrenceKey provide one | ||
| /// reusable way to pack a short (depth <= 2) ordinal chain into such a key. | ||
| module internal FSharp.Compiler.AbstractIL.EncMethodDebugInformation | ||
|
|
||
| /// Portable-PDB CustomDebugInformation kind GUIDs for the EnC blobs, copied verbatim | ||
| /// from roslyn/src/Dependencies/CodeAnalysis.Debugging/PortableCustomDebugInfoKinds.cs. | ||
| [<RequireQualifiedAccess>] | ||
| module PortableCustomDebugInfoKinds = | ||
|
|
||
| /// EnC Local Slot Map CDI kind. | ||
| val encLocalSlotMap: System.Guid | ||
|
|
||
| /// EnC Lambda and Closure Map CDI kind. | ||
| val encLambdaAndClosureMap: System.Guid | ||
|
|
||
| /// EnC State Machine State Map CDI kind. | ||
| val encStateMachineStateMap: System.Guid | ||
|
|
||
| /// Closure ordinal of a lambda that is lowered to a static (non-capturing) method. | ||
| /// Mirrors Roslyn's LambdaDebugInfo.StaticClosureOrdinal. | ||
| [<Literal>] | ||
| val StaticClosureOrdinal: int = -1 | ||
|
|
||
| /// Closure ordinal of a lambda closed over the 'this' pointer only. | ||
| /// Mirrors Roslyn's LambdaDebugInfo.ThisOnlyClosureOrdinal. | ||
| [<Literal>] | ||
| val ThisOnlyClosureOrdinal: int = -2 | ||
|
|
||
| /// Smallest valid closure ordinal. Mirrors Roslyn's LambdaDebugInfo.MinClosureOrdinal. | ||
| [<Literal>] | ||
| val MinClosureOrdinal: int = -2 | ||
|
|
||
| /// Method ordinal of a method that has no lambda map (an empty blob decodes to this). | ||
| /// Mirrors Roslyn's DebugId.UndefinedOrdinal. | ||
| [<Literal>] | ||
| val UndefinedMethodOrdinal: int = -1 | ||
|
|
||
| /// Largest synthesized-local kind serializable in the slot map: the kind is stored as | ||
| /// (kind + 1) in bits 0-5 of the leading byte (bit 6 is unused, bit 7 flags a trailing ordinal), and | ||
| /// Roslyn's reader recovers it with mask 0x3F, so only kinds 0..0x3E round-trip. | ||
| [<Literal>] | ||
| val MaxSerializableLocalKind: int = 0x3E | ||
|
|
||
| /// One slot in the EnC Local Slot Map: the local variable layout of a method body, | ||
| /// recorded so a later generation can map its locals onto the same slot indices. | ||
| [<RequireQualifiedAccess>] | ||
| type EncLocalSlotInfo = | ||
| /// A short-lived lowering temp: serialized as the single byte 0x00, carrying no | ||
| /// identity (a later generation never reuses it). | ||
| | Temp | ||
|
|
||
| /// A long-lived synthesized local. | ||
| /// kind: synthesized-local kind (Roslyn SynthesizedLocalKind value, 0..MaxSerializableLocalKind; | ||
| /// 0 = user-defined local). | ||
| /// syntaxOffset: caller-defined key of the declaring occurrence | ||
| /// (Roslyn: syntax offset of the local's declarator). | ||
| /// ordinal: zero-based disambiguator among slots sharing the same kind and offset (>= 0). | ||
| | Slot of kind: int * syntaxOffset: int * ordinal: int | ||
|
|
||
| /// One closure scope in the EnC Lambda and Closure Map. The closure's ordinal is its | ||
| /// index in EncMethodDebugInformation.Closures; lambdas reference closures by that index. | ||
| type EncClosureInfo = | ||
| { | ||
| /// Caller-defined key (Roslyn: syntax offset of the scope owning the closure). | ||
| SyntaxOffset: int | ||
| } | ||
|
|
||
| /// One lambda in the EnC Lambda and Closure Map. | ||
| type EncLambdaInfo = | ||
| { | ||
| /// Caller-defined key (Roslyn: syntax offset of the lambda body). | ||
| SyntaxOffset: int | ||
| /// Index into EncMethodDebugInformation.Closures of the closure holding the | ||
| /// lambda's captures, or StaticClosureOrdinal / ThisOnlyClosureOrdinal. | ||
| ClosureOrdinal: int | ||
| } | ||
|
|
||
| /// One suspension point in the EnC State Machine State Map. | ||
| type EncStateMachineStateInfo = | ||
| { | ||
| /// State machine state number assigned to the suspension point (may be negative: | ||
| /// Roslyn uses negative numbers for increasing-iteration finalize states). | ||
| StateNumber: int | ||
| /// Caller-defined key (Roslyn: syntax offset of the await/yield syntax node). | ||
| SyntaxOffset: int | ||
| } | ||
|
|
||
| /// Debugging information associated with a method, persisted by the compiler in the | ||
| /// Portable PDB to support Edit and Continue. Mirrors Roslyn's | ||
| /// EditAndContinueMethodDebugInformation. | ||
| type EncMethodDebugInformation = | ||
| { | ||
| /// Ordinal of the method within its generation (>= -1; UndefinedMethodOrdinal when absent). | ||
| MethodOrdinal: int | ||
| /// Local slot layout, in slot-index order (EnC Local Slot Map). | ||
| LocalSlots: EncLocalSlotInfo list | ||
| /// Closure scopes, in ordinal order (EnC Lambda and Closure Map). | ||
| Closures: EncClosureInfo list | ||
| /// Lambdas, in ordinal order (EnC Lambda and Closure Map). | ||
| Lambdas: EncLambdaInfo list | ||
| /// State machine suspension points (EnC State Machine State Map). | ||
| StateMachineStates: EncStateMachineStateInfo list | ||
| } | ||
|
|
||
| /// An empty map (no slots, lambdas, closures or states; undefined method ordinal). | ||
| static member Empty: EncMethodDebugInformation | ||
|
|
||
| /// Packs an ordinal chain (root-first enclosing ordinals, ending with the innermost | ||
| /// ordinal) into a deterministic int suitable for a "syntax offset" blob slot. Fails | ||
| /// closed (None) past the limits: chains deeper than 2, ordinals > 0xFFFF, or keys | ||
| /// exceeding the compressed-integer budget. | ||
| val tryEncodeOccurrenceKey: ordinalChain: int list -> int option | ||
|
|
||
| /// Unpacks an occurrence key produced by tryEncodeOccurrenceKey back into its | ||
| /// root-first ordinal chain. | ||
| val decodeOccurrenceKey: key: int -> int list | ||
|
|
||
| /// Serializes the EnC Local Slot Map blob for 'info', byte-for-byte as Roslyn's | ||
| /// SerializeLocalSlots. Returns the empty array when there are no slots (no CDI row | ||
| /// should be emitted then). | ||
| val serializeLocalSlots: info: EncMethodDebugInformation -> byte[] | ||
|
|
||
| /// Deserializes an EnC Local Slot Map blob, byte-for-byte as Roslyn's UncompressSlotMap. | ||
| /// An empty (or null) blob yields no slots. | ||
| val deserializeLocalSlots: blob: byte[] -> EncLocalSlotInfo list | ||
|
|
||
| /// Serializes the EnC Lambda and Closure Map blob for 'info', byte-for-byte as Roslyn's | ||
| /// SerializeLambdaMap. Returns the empty array when there are no lambdas and no closures | ||
| /// (Roslyn's MetadataWriter skips the CDI row in that case; note the method ordinal is | ||
| /// then not persisted and decodes back as UndefinedMethodOrdinal). | ||
| val serializeLambdaMap: info: EncMethodDebugInformation -> byte[] | ||
|
|
||
| /// Deserializes an EnC Lambda and Closure Map blob, byte-for-byte as Roslyn's | ||
| /// UncompressLambdaMap. An empty (or null) blob yields (UndefinedMethodOrdinal, [], []). | ||
| val deserializeLambdaMap: blob: byte[] -> int * EncClosureInfo list * EncLambdaInfo list | ||
|
|
||
| /// Serializes the EnC State Machine State Map blob for 'info', byte-for-byte as | ||
| /// Roslyn's SerializeStateMachineStates: entries are sorted by syntax offset (stably, | ||
| /// preserving relative order of equal offsets, which encodes the per-offset relative | ||
| /// ordinal). Returns the empty array when there are no states (no CDI row then). | ||
| val serializeStateMachineStates: info: EncMethodDebugInformation -> byte[] | ||
|
|
||
| /// Deserializes an EnC State Machine State Map blob, byte-for-byte as Roslyn's | ||
| /// UncompressStateMachineStates (including the ordered-by-offset and <= 256-per-offset | ||
| /// validations). An empty (or null) blob yields no states. | ||
| val deserializeStateMachineStates: blob: byte[] -> EncStateMachineStateInfo list | ||
|
|
||
| /// Deserializes EnC method debug information from the three blobs (any of which may be | ||
| /// null or empty). Mirrors Roslyn's EditAndContinueMethodDebugInformation.Create. | ||
| val deserialize: | ||
| slotMapBlob: byte[] -> lambdaMapBlob: byte[] -> stateMachineStateMapBlob: byte[] -> EncMethodDebugInformation | ||
|
|
||
| /// Decodes every method-level EnC CustomDebugInformation row of a portable PDB image into | ||
| /// per-method EnC debug information, keyed by MethodDef token (0x06xxxxxx). The CDI parent | ||
| /// of the EnC rows is always a MethodDef handle, so token keying is unambiguous. | ||
| /// Fail safe: a null/empty or non-PDB image yields the empty map, and a method whose | ||
| /// blobs do not decode is omitted rather than guessed. | ||
| val readEncMethodDebugInfoFromPortablePdb: pdbBytes: byte[] -> Map<int, EncMethodDebugInformation> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.