[GH-ISSUE #1940] inspect_managed_artifact rescans TypeDef rows for every local attribute constructor (quadratic) #645

Closed
opened 2026-10-15 18:26:53 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @kaoru0822-kitauji on GitHub (Oct 11, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/1940

Observed behavior

inspect_managed_artifact / rea inspect-managed-artifact decodes custom attributes in O(attributes × TypeDefs) time. For each CustomAttribute whose constructor is a local MethodDef (or a MemberRef whose parent is a MethodDef), declaringTypeForMethod in src/dotnet/ManagedMetadataInventoryRows.ts scans the TypeDef table from row 1 and builds row cursors until it finds the owning type.

Since #1897, repeated row cursors return early from reserveRow, so the decode budget no longer bounds this repeated work (Codex raised this as a P1 on #1897). The scan runs synchronously, which blocks the CLI or MCP server event loop for the whole time.

Measured on main d802304c5 with buildManagedPeFixture (N empty TypeDefs ahead of the attribute type, N attributes whose constructor is MethodDef 1, so each lookup scans every TypeDef row):

N types = N attributes file size inspectManagedArtifactBytes
1,000 31 KB 159 ms
4,000 130 KB 2.3 s
8,000 289 KB 9.7 s
16,000 584 KB 38.1 s

Time grows about 4× each time N doubles. Coverage stays complete, so the result is correct, only slow. Larger table counts in a few MB of crafted metadata would run for many minutes.

Reproduction

const bytes = buildManagedPeFixture({
  additionalEmptyTypes: 16_000,
  customAttributeRowCount: 16_000,
  customAttributeTypeRaw: (1 << 3) | 2,
  typeName: "LookupAttribute",
  extendsRaw: 0,
  methodName: ".ctor",
  methodFlags: 0x1886,
  methodSignature: Buffer.from([0x20, 0x01, 0x01, 0x0e]),
});
inspectManagedArtifactBytes(bytes, managedPeFixtureTarget(bytes)); // ~38 s

Expected behavior

Resolve each constructor's declaring type without rescanning the TypeDef table, keeping the current first-match owner for malformed, overlapping method lists. The same input should take milliseconds.

Notes

Open PR #1706 (a feature PR) includes a similar index. It conflicts with main, predates pointer-table ownership (#1886), and still rescans per attribute when TypeDef method lists are unordered.

Originally created by @kaoru0822-kitauji on GitHub (Oct 11, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1940 ### Observed behavior `inspect_managed_artifact` / `rea inspect-managed-artifact` decodes custom attributes in O(attributes × TypeDefs) time. For each `CustomAttribute` whose constructor is a local `MethodDef` (or a `MemberRef` whose parent is a `MethodDef`), `declaringTypeForMethod` in `src/dotnet/ManagedMetadataInventoryRows.ts` scans the TypeDef table from row 1 and builds row cursors until it finds the owning type. Since #1897, repeated row cursors return early from `reserveRow`, so the decode budget no longer bounds this repeated work (Codex raised this as a P1 on #1897). The scan runs synchronously, which blocks the CLI or MCP server event loop for the whole time. Measured on main `d802304c5` with `buildManagedPeFixture` (N empty TypeDefs ahead of the attribute type, N attributes whose constructor is MethodDef 1, so each lookup scans every TypeDef row): | N types = N attributes | file size | `inspectManagedArtifactBytes` | | ---: | ---: | ---: | | 1,000 | 31 KB | 159 ms | | 4,000 | 130 KB | 2.3 s | | 8,000 | 289 KB | 9.7 s | | 16,000 | 584 KB | 38.1 s | Time grows about 4× each time N doubles. Coverage stays `complete`, so the result is correct, only slow. Larger table counts in a few MB of crafted metadata would run for many minutes. ### Reproduction ```ts const bytes = buildManagedPeFixture({ additionalEmptyTypes: 16_000, customAttributeRowCount: 16_000, customAttributeTypeRaw: (1 << 3) | 2, typeName: "LookupAttribute", extendsRaw: 0, methodName: ".ctor", methodFlags: 0x1886, methodSignature: Buffer.from([0x20, 0x01, 0x01, 0x0e]), }); inspectManagedArtifactBytes(bytes, managedPeFixtureTarget(bytes)); // ~38 s ``` ### Expected behavior Resolve each constructor's declaring type without rescanning the TypeDef table, keeping the current first-match owner for malformed, overlapping method lists. The same input should take milliseconds. ### Notes Open PR #1706 (a feature PR) includes a similar index. It conflicts with main, predates pointer-table ownership (#1886), and still rescans per attribute when TypeDef method lists are unordered.
bsakaguchi 2026-10-15 18:26:53 +00:00
  • closed this issue
  • added the
    bug
    label
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#645
No description provided.