Skip to content

Add IlProjection: new-pipeline IL views (raw/structured) with resolved operands#615

Merged
richlander merged 2 commits into
mainfrom
feature/il-projection
Jun 18, 2026
Merged

Add IlProjection: new-pipeline IL views (raw/structured) with resolved operands#615
richlander merged 2 commits into
mainfrom
feature/il-projection

Conversation

@richlander

Copy link
Copy Markdown
Owner

What

IlProjection renders the ground-truth IL views (Raw, Structured) from the replacement pipeline's materialized method data off a MetadataSource — the new-pipeline backing for the annotated-IL view, replacing the old MethodBodyContextMethodAnalysisAnnotatedILEmitter contract.

IlProjection.Project(MetadataSource source, string type, string method,
                     IlProjectionDepth depth, int overloadIndex = 0, bool publicOnly = false)
    -> DecompilerResult

Design

  • One analysis, not a parallel one. Operands resolve through the importer's own token resolvers (ResolveMethod/ResolveField/ResolveTypeToken), and block structure reuses the importer's FindLeaders — so the IL views share the pipeline's analysis rather than running a second CFG/stack pass (the duplication the old MethodAnalysis represented).
  • Exception-safe by construction. DecompilerResult.Run wraps it; a malformed read or resolver failure surfaces as a diagnostic, never a throw. Operand resolution falls back to raw token hex per-instruction, so the structure stays ground truth even if one token can't resolve.
  • SRM-only, lives in Pipeline/ (no ILInspector.Metadata dependency).

Output

System.String.IsNullOrEmpty, Raw — operands resolved, branch targets computed:

IL_0000: ldarg.0
IL_0001: brfalse.s IL_000D
IL_0004: callvirt string::get_Length()
IL_000A: ceq
IL_000C: ret
IL_000D: ldc.i4.1

Structured block boundaries (IL_0000/IL_0003/IL_000D) match the old AnnotatedILEmitter exactly (Block_0 0000-0002, Block_1 0003-000C, Block_2 000D-000E).

Scope

  • Exposes three IrImporter helpers as internal (FindLeaders, CallerScope, ResolveTypeToken).
  • Non-disruptive: no consumers migrated yet — it dual-runs beside the old emitter.

Follow-ups (per the retirement plan)

  1. Typed depth — per-instruction stack types, sourced from the importer's typed operand stack via a per-instruction trace (the one genuinely new piece; spiked separately).
  2. Migrate consumersMemberCodeProvider (annotated-IL view) and the harness DumpStages onto IlProjection, then delete MethodBodyContext/MethodAnalysis/AnnotatedILEmitter, gated by a corpus parity diff of new-vs-old annotated IL.

Tests

4 new (IlProjectionTests): offsets/opcodes, resolved call operand (not raw hex), branch-block labels, .try/catch markers. ILInspector.Decompiler.Tests 430 pass.

🤖 Generated with Claude Code

richlander and others added 2 commits June 18, 2026 06:23
Renders the ground-truth IL projections (Raw, Structured) from the replacement
pipeline's materialized method data off a MetadataSource — the new-pipeline
backing for the annotated-IL view, replacing the old MethodBodyContext ->
MethodAnalysis -> AnnotatedILEmitter contract.

Operands resolve through the importer's own token resolvers (ResolveMethod /
ResolveField / ResolveTypeToken) so callvirt/ldfld/ldstr render as names, not raw
tokens; block structure reuses the importer's FindLeaders, so the views share one
analysis with the IR import rather than running a parallel CFG/stack pass.
Exception-safe by construction (DecompilerResult.Run): a malformed read or a
resolver failure surfaces as a diagnostic, never a throw; operand resolution
falls back to raw token hex per-instruction.

Exposes three IrImporter helpers as internal (FindLeaders, CallerScope,
ResolveTypeToken). Non-disruptive: no consumers migrated yet, so it dual-runs
beside the old emitter. The per-instruction stack-typed depth and the consumer
migration (MemberCodeProvider, harness DumpStages) + deletion of the old infra
are follow-ups.

Tests: 4 new (offsets/opcodes, resolved call operand, block labels, .try/catch
markers); ILInspector.Decompiler.Tests 430 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Structured_LabelsBranchBlocks anchored a regex with $ over output rendered with
Environment.NewLine; on Windows the trailing \r before the line break made the
$ anchor miss (0 matches). Normalize with ReplaceLineEndings("\n") in the test
helper, the repo's convention for line-anchored assertions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@richlander richlander merged commit e1ce43a into main Jun 18, 2026
10 checks passed
@richlander richlander deleted the feature/il-projection branch June 18, 2026 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant