refactor(managed): centralize method implementation flag facts

Have the MethodDef row owner derive code type, managed kind and the P/Invoke flag for both body decoding and native-boundary inventory. Remove duplicate masks and unreachable fallbacks while preserving raw flags, zero-RVA precedence and ImplMap-specific declaration policy.

Validation: 280 focused source, CLI and MCP tests passed, including cross-workflow flag combinations and declaration/body policy boundaries. Typecheck and lint passed.
This commit is contained in:
morluto committed 2026-10-27 02:34:59 +08:00
1 parent 8ced814b96
commit 1f5c5c31c1
5 files changed
+250 -60

No files matched your search

+2 -1
View File
@@ -366,6 +366,7 @@ export const parseMethods = ({
flags,
nameIndex,
signatureIndex,
implementation,
} = readMethodDefinitionRow(bytes, layout, row);
const name = readMetadataString(
bytes,
@@ -408,7 +409,7 @@ export const parseMethods = ({
),
});
const read = methodBody(bytes, pe, rva, {
implementation: { implFlags, flags },
implementation,
budget: managedDecodeBudget(layout),
metadataLayout: layout,
methodToken: token,
+5 -6
View File
@@ -1,3 +1,4 @@
import type { ManagedMethodImplementationFlags } from "./ManagedMethodDefinitionRows.js";
import {
admitManagedProjection,
type ManagedDecodeBudget,
@@ -84,10 +85,7 @@ const readMethodBodyHeader = (
};
export interface MethodBodyContext {
readonly implementation: {
readonly implFlags: number;
readonly flags: number;
};
readonly implementation: ManagedMethodImplementationFlags;
readonly budget: ManagedDecodeBudget;
readonly metadataLayout: ManagedMetadataLayout;
readonly methodToken: string;
@@ -122,8 +120,9 @@ export const methodBody = (
issues,
};
const native =
(context.implementation.implFlags & 7) !== 0 ||
(context.implementation.flags & 0x2000) !== 0;
context.implementation.codeType !== "il" ||
context.implementation.managedKind !== "managed" ||
context.implementation.pinvokeFlag;
if (native) {
const issue =
"CIL decoding is unavailable for native, runtime, unmanaged, or P/Invoke implementations";
+21 -3
View File
@@ -1,6 +1,15 @@
import type { ManagedMetadataLayout } from "./ManagedMetadataLayout.js";
import { metadataRowCursor, metadataToken } from "./ManagedMetadataHeaps.js";
/** Meaning of the MethodDef implementation bits, independent of RVA or ImplMap evidence. */
export interface ManagedMethodImplementationFlags {
readonly codeType: "il" | "native" | "optil" | "runtime";
readonly managedKind: "managed" | "unmanaged";
readonly pinvokeFlag: boolean;
}
const METHOD_CODE_TYPES = ["il", "native", "optil", "runtime"] as const;
/** Scalar MethodDef facts; consumers own heap admission and method selection. */
export const readMethodDefinitionRow = (
bytes: Buffer,
@@ -14,16 +23,25 @@ export const readMethodDefinitionRow = (
readonly flags: number;
readonly nameIndex: number;
readonly signatureIndex: number;
readonly implementation: ManagedMethodImplementationFlags;
} => {
// Layout admission bounds the whole fixed-width row before a cursor is created.
const cursor = metadataRowCursor(bytes, layout, 6, row);
const rva = cursor.readUInt32();
const implFlags = cursor.readUInt16();
const flags = cursor.readUInt16();
return {
token: metadataToken(6, row),
rowOffset: cursor.start,
rva: cursor.readUInt32(),
implFlags: cursor.readUInt16(),
flags: cursor.readUInt16(),
rva,
implFlags,
flags,
nameIndex: cursor.readIndex(layout.stringIndexSize),
signatureIndex: cursor.readIndex(layout.blobIndexSize),
implementation: {
codeType: METHOD_CODE_TYPES[(implFlags & 0x0003) as 0 | 1 | 2 | 3],
managedKind: (implFlags & 0x0004) === 0 ? "managed" : "unmanaged",
pinvokeFlag: (flags & 0x2000) !== 0,
},
};
};
+35 -50
View File
@@ -1,4 +1,7 @@
import { readMethodDefinitionRow } from "./ManagedMethodDefinitionRows.js";
import {
readMethodDefinitionRow,
type ManagedMethodImplementationFlags,
} from "./ManagedMethodDefinitionRows.js";
import {
admitManagedProjection,
managedDecodeBudget,
@@ -31,15 +34,24 @@ type NativeImport = ManagedNativeBoundaryInspection["pinvoke_imports"][number];
type NativeImplementation =
ManagedNativeBoundaryInspection["native_implementations"][number];
interface MemberCore {
type MemberCore = {
readonly token: string;
readonly rowOffset: number;
readonly kind: NativeImport["member_kind"];
readonly name: string;
readonly flags: number;
readonly implFlags: number | null;
readonly rva: number | null;
}
} & (
| {
readonly kind: "field";
readonly implFlags: null;
readonly rva: null;
}
| {
readonly kind: "method";
readonly implFlags: number;
readonly rva: number;
readonly implementation: ManagedMethodImplementationFlags;
}
);
const flagsHex = (value: number): string =>
`0x${value.toString(16).padStart(4, "0")}`;
@@ -114,8 +126,15 @@ export const parseMethods = (
const table = layout.table(6);
for (let row = 1; row <= (table?.rowCount ?? 0); row += 1) {
readManagedValue(() => {
const { token, rowOffset, rva, implFlags, flags, nameIndex } =
readMethodDefinitionRow(bytes, layout, row);
const {
token,
rowOffset,
rva,
implFlags,
flags,
nameIndex,
implementation,
} = readMethodDefinitionRow(bytes, layout, row);
const name = readMetadataString(bytes, layout, nameIndex, heapExtent);
methods.set(token, {
token,
@@ -125,6 +144,7 @@ export const parseMethods = (
flags,
implFlags,
rva,
implementation,
});
}, issues);
}
@@ -296,67 +316,32 @@ export const nativeImplementations = (
const implementations: NativeImplementation[] = [];
for (const method of methods) {
if (method.kind !== "method") continue;
const implFlags = method.implFlags ?? 0;
const codeType = codeTypeFor(implFlags);
const managedKind = managedKindFor(implFlags);
const pinvokeDeclared =
pinvokeTokens.has(method.token) || (method.flags & 0x2000) !== 0;
const { codeType, managedKind, pinvokeFlag } = method.implementation;
const pinvokeDeclared = pinvokeTokens.has(method.token) || pinvokeFlag;
if (!pinvokeDeclared && codeType === "il" && managedKind === "managed")
continue;
implementations.push({
token: method.token,
row_offset: method.rowOffset,
name: method.name,
rva: method.rva ?? 0,
rva: method.rva,
flags: method.flags,
impl_flags: implFlags,
impl_flags: method.implFlags,
code_type: codeType,
managed_kind: managedKind,
pinvoke_declared: pinvokeDeclared,
boundary_kind: boundaryKind(pinvokeDeclared, codeType, managedKind),
body_interpretation:
codeType === "il" && managedKind === "managed" && !pinvokeDeclared
? "managed-cil"
: method.rva === 0
? "not-file-backed"
: "native-or-runtime",
method.rva === 0 ? "not-file-backed" : "native-or-runtime",
});
}
return implementations;
};
const codeTypeFor = (implFlags: number): NativeImplementation["code_type"] => {
switch (implFlags & 0x0003) {
case 0x0000:
return "il";
case 0x0001:
return "native";
case 0x0002:
return "optil";
case 0x0003:
return "runtime";
default:
return "unknown";
}
};
const managedKindFor = (
implFlags: number,
): NativeImplementation["managed_kind"] => {
switch (implFlags & 0x0004) {
case 0x0000:
return "managed";
case 0x0004:
return "unmanaged";
default:
return "unknown";
}
};
const boundaryKind = (
pinvokeDeclared: boolean,
codeType: NativeImplementation["code_type"],
managedKind: NativeImplementation["managed_kind"],
codeType: ManagedMethodImplementationFlags["codeType"],
managedKind: ManagedMethodImplementationFlags["managedKind"],
): NativeImplementation["boundary_kind"] => {
if (pinvokeDeclared) return "pinvoke";
if (codeType === "native") return "native-body";
@@ -1,3 +1,8 @@
import {
metadataRowOffset,
readManagedMetadataLayout,
} from "./ManagedMetadataLayout.js";
import { readManagedPeLayout } from "./ManagedPeReader.js";
import { describe, expect, it } from "vitest";
import { createHash } from "node:crypto";
@@ -411,3 +416,185 @@ describe("managed PE fixture layout", () => {
);
});
});
const inspectImplementationFixture = ({
implFlags,
methodFlags = 0x0016,
zeroRva = false,
implMap = false,
}: {
implFlags: number;
methodFlags?: number;
zeroRva?: boolean;
implMap?: boolean;
}) => {
const bytes = buildManagedPeFixture({
methodFlags,
...(implMap ? { pinvoke: {} } : {}),
});
const pe = readManagedPeLayout(bytes);
if (pe.cli === null) throw new Error("Expected CLI fixture");
const { rva, size } = pe.cli.metadata;
const layout = readManagedMetadataLayout(
bytes,
pe.rvaToOffset(rva, size, "cli.metadata"),
size,
);
const row = metadataRowOffset(layout, 6, 1);
bytes.writeUInt16LE(implFlags, row + 4);
if (zeroRva) bytes.writeUInt32LE(0, row);
const target = managedPeFixtureTarget(bytes);
return {
members: inspectManagedMembersBytes(bytes, target),
boundaries: inspectManagedNativeBoundariesBytes(bytes, target),
};
};
it.each([
{ bits: 0, codeType: "il", managedKind: "managed", boundary: null },
{
bits: 1,
codeType: "native",
managedKind: "managed",
boundary: "native-body",
},
{
bits: 2,
codeType: "optil",
managedKind: "managed",
boundary: "mixed-or-unknown",
},
{
bits: 3,
codeType: "runtime",
managedKind: "managed",
boundary: "runtime-provided",
},
{
bits: 4,
codeType: "il",
managedKind: "unmanaged",
boundary: "unmanaged-method",
},
{
bits: 5,
codeType: "native",
managedKind: "unmanaged",
boundary: "native-body",
},
{
bits: 6,
codeType: "optil",
managedKind: "unmanaged",
boundary: "unmanaged-method",
},
{
bits: 7,
codeType: "runtime",
managedKind: "unmanaged",
boundary: "runtime-provided",
},
])(
"publishes implementation facts consistently with body admission for low flags $bits",
({ bits, codeType, managedKind, boundary }) => {
// NoInlining, Synchronized and PreserveSig do not change code type or managed kind.
for (const highBits of [0, 0x00a8]) {
const implFlags = bits | highBits;
const { members, boundaries } = inspectImplementationFixture({
implFlags,
});
expect(members.methods[0]?.body.status).toBe(
boundary === null ? "present" : "partial",
);
if (boundary === null) {
expect(boundaries.native_implementations).toEqual([]);
expect(members.methods[0]?.body.il_sha256).toEqual(expect.any(String));
} else {
expect(boundaries.native_implementations).toMatchObject([
{
token: members.methods[0]?.token,
impl_flags: implFlags,
code_type: codeType,
managed_kind: managedKind,
pinvoke_declared: false,
boundary_kind: boundary,
body_interpretation: "native-or-runtime",
},
]);
expect(members.methods[0]?.body.il_sha256).toBeNull();
expect(members.methods[0]?.body.issue).toMatch(/CIL/u);
}
}
},
);
it.each([
{
name: "PInvoke flag on managed IL",
implFlags: 0,
methodFlags: 0x2016,
zeroRva: false,
implMap: false,
status: "partial",
codeType: "il",
managedKind: "managed",
},
{
name: "PInvoke flag on unmanaged OPTIL",
implFlags: 6,
methodFlags: 0x2016,
zeroRva: false,
implMap: false,
status: "partial",
codeType: "optil",
managedKind: "unmanaged",
},
{
name: "absent RVA before PInvoke/runtime classification",
implFlags: 7,
methodFlags: 0x2016,
zeroRva: true,
implMap: false,
status: "absent",
codeType: "runtime",
managedKind: "unmanaged",
},
{
name: "ImplMap without the PInvoke flag",
implFlags: 0,
methodFlags: 0x0016,
zeroRva: false,
implMap: true,
status: "present",
codeType: "il",
managedKind: "managed",
},
])(
"retains distinct native declaration and body policies: $name",
(scenario) => {
const { members, boundaries } = inspectImplementationFixture(scenario);
expect(boundaries.native_implementations).toMatchObject([
{
token: members.methods[0]?.token,
flags: scenario.methodFlags,
code_type: scenario.codeType,
managed_kind: scenario.managedKind,
pinvoke_declared: true,
boundary_kind: "pinvoke",
body_interpretation: scenario.zeroRva
? "not-file-backed"
: "native-or-runtime",
},
]);
expect(boundaries.pinvoke_imports).toHaveLength(scenario.implMap ? 1 : 0);
expect(members.methods[0]?.body.status).toBe(scenario.status);
if (scenario.zeroRva)
expect(members.methods[0]?.body).toMatchObject({
rva: 0,
issue: null,
il_sha256: null,
});
if (scenario.status === "present")
expect(members.methods[0]?.body.il_sha256).toEqual(expect.any(String));
},
);