fix(dotnet): honor bounded method row selections
This commit is contained in:
parent
36db19c59c
commit
9fe54c4073
|
|
@ -17,6 +17,7 @@ import { signature, type ManagedMethod } from "./ManagedMemberInspectorCore.js";
|
|||
import { ManagedReaderFailure } from "./ManagedReaderFailure.js";
|
||||
import { managedMemberInspectionSchema } from "../domain/managed/managedArtifact.js";
|
||||
import { comparisonLimitations } from "../domain/managed/managedMemberComparisonCoverage.js";
|
||||
import { buildManagedPeFixture as buildMultiMethodManagedPeFixture } from "../../scripts/fixtures/managed/pe.mjs";
|
||||
|
||||
import {
|
||||
buildManagedPeFixture,
|
||||
|
|
@ -1735,3 +1736,61 @@ describe("managed member type selection", () => {
|
|||
expect(result.member_refs).toEqual(everything.member_refs);
|
||||
});
|
||||
});
|
||||
|
||||
describe("managed member method paging", () => {
|
||||
it("returns only the requested MethodDef rows", () => {
|
||||
const image = buildMultiMethodManagedPeFixture({
|
||||
methods: [
|
||||
{ name: "First", body: Buffer.from([0x2a]) },
|
||||
{ name: "Second", body: Buffer.from([0x2a]) },
|
||||
{ name: "Third", body: Buffer.from([0x2a]) },
|
||||
],
|
||||
});
|
||||
const all = inspectManagedMembersBytes(
|
||||
image,
|
||||
managedPeFixtureTarget(image),
|
||||
);
|
||||
expect(all.methods.map(({ name }) => name)).toEqual([
|
||||
"First",
|
||||
"Second",
|
||||
"Third",
|
||||
]);
|
||||
|
||||
const page = inspectManagedMembersBytes(
|
||||
image,
|
||||
managedPeFixtureTarget(image),
|
||||
{ methodOffset: 1, methodLimit: 1 },
|
||||
);
|
||||
|
||||
expect(page.methods.map(({ name, token }) => ({ name, token }))).toEqual([
|
||||
{ name: "Second", token: "0x06000002" },
|
||||
]);
|
||||
expect(page.coverage.state).toBe("complete");
|
||||
|
||||
const offsetOnly = inspectManagedMembersBytes(
|
||||
image,
|
||||
managedPeFixtureTarget(image),
|
||||
{ methodOffset: 1 },
|
||||
);
|
||||
expect(offsetOnly.methods.map(({ name }) => name)).toEqual([
|
||||
"Second",
|
||||
"Third",
|
||||
]);
|
||||
|
||||
const emptyPage = inspectManagedMembersBytes(
|
||||
image,
|
||||
managedPeFixtureTarget(image),
|
||||
{ methodOffset: 1, methodLimit: 0 },
|
||||
);
|
||||
expect(emptyPage.methods).toEqual([]);
|
||||
});
|
||||
|
||||
it("rejects invalid method page bounds", () => {
|
||||
const image = buildManagedPeFixture();
|
||||
expect(() =>
|
||||
inspectManagedMembersBytes(image, managedPeFixtureTarget(image), {
|
||||
methodOffset: -1,
|
||||
}),
|
||||
).toThrow(RangeError);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -160,12 +160,26 @@ export const inspectManagedMembersBytes = (
|
|||
target: BinaryTarget,
|
||||
{
|
||||
typeNames,
|
||||
methodOffset = 0,
|
||||
methodLimit = Number.MAX_SAFE_INTEGER,
|
||||
userStrings: readUserStrings = true,
|
||||
}: {
|
||||
readonly typeNames?: readonly string[];
|
||||
/** Zero-based offset into MethodDef rows. */
|
||||
readonly methodOffset?: number;
|
||||
readonly methodLimit?: number;
|
||||
readonly userStrings?: boolean;
|
||||
} = {},
|
||||
): ManagedMemberInspection => {
|
||||
if (
|
||||
!Number.isSafeInteger(methodOffset) ||
|
||||
methodOffset < 0 ||
|
||||
!Number.isSafeInteger(methodLimit) ||
|
||||
methodLimit < 0
|
||||
)
|
||||
throw new RangeError(
|
||||
"methodOffset and methodLimit must be non-negative safe integers",
|
||||
);
|
||||
const pe = readManagedPeLayout(bytes);
|
||||
if (pe.cli === null)
|
||||
return unavailable(target, bytes, pe.cliIssue, typeNames);
|
||||
|
|
@ -196,6 +210,8 @@ export const inspectManagedMembersBytes = (
|
|||
pe,
|
||||
ranges,
|
||||
selected,
|
||||
methodOffset,
|
||||
methodLimit,
|
||||
});
|
||||
appendManagedParseIssues(issues, fields.issues, methods.issues);
|
||||
// #US literals sit beside the member inventory and carry their own
|
||||
|
|
|
|||
|
|
@ -257,6 +257,8 @@ interface ParseMethodsInput {
|
|||
readonly pe: ManagedPeLayout;
|
||||
readonly ranges: readonly TypeRange[];
|
||||
readonly selected?: SelectedTypeTokens;
|
||||
readonly methodOffset?: number;
|
||||
readonly methodLimit?: number;
|
||||
}
|
||||
|
||||
export const parseMethods = ({
|
||||
|
|
@ -265,6 +267,8 @@ export const parseMethods = ({
|
|||
pe,
|
||||
ranges,
|
||||
selected = null,
|
||||
methodOffset = 0,
|
||||
methodLimit = Number.MAX_SAFE_INTEGER,
|
||||
}: ParseMethodsInput): {
|
||||
readonly methods: readonly ManagedMethod[];
|
||||
readonly core: ReadonlyMap<string, MemberCore>;
|
||||
|
|
@ -292,12 +296,14 @@ export const parseMethods = ({
|
|||
? undefined
|
||||
: readManagedValue(() => ownerRow(row), issues);
|
||||
const declared = owner === undefined ? undefined : ranges[owner - 1];
|
||||
const inPage = row > methodOffset && row - methodOffset <= methodLimit;
|
||||
const mayBeSelected =
|
||||
selected === null ||
|
||||
owner === undefined ||
|
||||
isSelected(selected, declared) ||
|
||||
// A type whose name is unreadable may be one the caller requested.
|
||||
declared?.fullName === null;
|
||||
inPage &&
|
||||
(selected === null ||
|
||||
owner === undefined ||
|
||||
isSelected(selected, declared) ||
|
||||
// A type whose name is unreadable may be one the caller requested.
|
||||
declared?.fullName === null);
|
||||
const produced = methods.length;
|
||||
readManagedValue(() => {
|
||||
const cursor = metadataRowCursor(bytes, layout, 6, row);
|
||||
|
|
@ -318,6 +324,7 @@ export const parseMethods = ({
|
|||
kind: "method",
|
||||
} as const;
|
||||
core.set(token, member);
|
||||
if (!inPage) return;
|
||||
if (!isSelected(selected, declared)) return;
|
||||
const sig = readMetadataBlob(
|
||||
bytes,
|
||||
|
|
|
|||
Loading…
Reference in New Issue