mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
refactor(managed): unify coded-index identity and validation
Decode metadata coded indexes once into null, valid or invalid facts. Migrate all existing consumers and remove parallel token and failure-reason decoders. Keep required-null policy with each column, preserving tokens, reason text, offsets, issue ordering and readable partial metadata. Retain known table/row coordinates even when invalid; TypeRef regressions preserve row-zero and out-of-range enclosing facts without granting validity. Attribute naming consumes the same validated constructor coordinate. Validation: metadata/member/native-boundary/signature source suites and public CLI/MCP recovery workflows passed; static, docs and duplication checks passed.
This commit is contained in:
@@ -17,6 +17,8 @@ import { edges } from "./ManagedMemberRows.js";
|
||||
import { parseUserStrings } from "./ManagedUserStrings.js";
|
||||
import { signature, type ManagedMethod } from "./ManagedMemberInspectorCore.js";
|
||||
import { ManagedReaderFailure } from "./ManagedReaderFailure.js";
|
||||
import { managedTypeReferences } from "./ManagedTypeReferences.js";
|
||||
import type { ManagedParseIssue } from "../domain/managed/managedArtifact.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";
|
||||
@@ -637,6 +639,38 @@ describe("managed edge target declaring types", () => {
|
||||
});
|
||||
|
||||
describe("managed TypeRef resolution scopes", () => {
|
||||
it.each([0, 9])(
|
||||
"retains invalid enclosing TypeRef row %s without granting scope validity",
|
||||
(enclosingRow) => {
|
||||
const bytes = buildManagedPeFixture({
|
||||
typeRefScopeRaw: enclosingRow * 4 + 3,
|
||||
});
|
||||
const pe = readManagedPeLayout(bytes);
|
||||
if (pe.cli === null) throw new Error("fixture has no CLI header");
|
||||
const { rva, size } = pe.cli.metadata;
|
||||
const layout = readManagedMetadataLayout(
|
||||
bytes,
|
||||
pe.rvaToOffset(rva, size, "metadata"),
|
||||
size,
|
||||
);
|
||||
const issues: ManagedParseIssue[] = [];
|
||||
const reference = managedTypeReferences(bytes, layout, issues)(1);
|
||||
|
||||
expect(reference).toMatchObject({ enclosingRow, scopeValid: false });
|
||||
expect(issues).toMatchObject([
|
||||
{
|
||||
code: "invalid-row",
|
||||
scope: "metadata.TypeRef:0x01000001",
|
||||
detail: expect.stringContaining(
|
||||
enclosingRow === 0
|
||||
? "coded index selects row zero in table 1"
|
||||
: "coded index selects row 9 in table 1, which has 1 rows",
|
||||
),
|
||||
},
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
// AssemblyRef 9 does not exist.
|
||||
["an invalid", (9 << 2) | 2, "is invalid"],
|
||||
|
||||
@@ -23,8 +23,7 @@ import type {
|
||||
} from "./ManagedMetadataLayout.js";
|
||||
import {
|
||||
metadataRowCursor,
|
||||
metadataCodedToken,
|
||||
metadataCodedTokenInvalidReason,
|
||||
decodeMetadataCodedIndex,
|
||||
metadataToken,
|
||||
readMetadataString,
|
||||
sha256Bytes,
|
||||
@@ -242,18 +241,18 @@ export const parseTypes = (
|
||||
definition.methodStart,
|
||||
definition.methodEnd,
|
||||
);
|
||||
const extendsReason = metadataCodedTokenInvalidReason(
|
||||
const extendsIndex = decodeMetadataCodedIndex(
|
||||
extendsRaw,
|
||||
2,
|
||||
[2, 1, 27],
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (extendsReason !== null)
|
||||
if (extendsIndex.kind === "invalid")
|
||||
issues.push({
|
||||
code: "invalid-row",
|
||||
scope: `metadata.TypeDef:${token}`,
|
||||
offset: extendsOffset,
|
||||
detail: `TypeDef Extends coded index 0x${extendsRaw.toString(16)} is invalid: ${extendsReason}`,
|
||||
detail: `TypeDef Extends coded index 0x${extendsRaw.toString(16)} is invalid: ${extendsIndex.reason}`,
|
||||
});
|
||||
const enclosingRow = nesting.enclosing.get(row);
|
||||
items.push({
|
||||
@@ -263,12 +262,7 @@ export const parseTypes = (
|
||||
name,
|
||||
full_name: definition.fullName,
|
||||
flags,
|
||||
extends_token: metadataCodedToken(
|
||||
extendsRaw,
|
||||
2,
|
||||
[2, 1, 27],
|
||||
layout.rowCounts,
|
||||
),
|
||||
extends_token: extendsIndex.kind === "valid" ? extendsIndex.token : null,
|
||||
enclosing_type_token:
|
||||
enclosingRow === undefined ? null : metadataToken(2, enclosingRow),
|
||||
name_sha256: null,
|
||||
@@ -530,20 +524,17 @@ const readTypeOperand = (
|
||||
): ReturnType<typeof readCompressed> => {
|
||||
const token = readCompressed(blob, offset);
|
||||
if (layout !== undefined) {
|
||||
const reason =
|
||||
token.value === 0
|
||||
? "type operand is null"
|
||||
: metadataCodedTokenInvalidReason(
|
||||
token.value,
|
||||
2,
|
||||
tables,
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (reason !== null)
|
||||
const decoded = decodeMetadataCodedIndex(
|
||||
token.value,
|
||||
2,
|
||||
tables,
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (decoded.kind !== "valid")
|
||||
throw managedFailure(
|
||||
"invalid-row",
|
||||
"signature",
|
||||
`TypeDefOrRefOrSpec operand 0x${token.value.toString(16)} is invalid: ${reason}`,
|
||||
`TypeDefOrRefOrSpec operand 0x${token.value.toString(16)} is invalid: ${decoded.kind === "null" ? "type operand is null" : decoded.reason}`,
|
||||
offset,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ import {
|
||||
import {
|
||||
metadataToken,
|
||||
metadataRowCursor,
|
||||
metadataCodedToken,
|
||||
metadataCodedTokenInvalidReason,
|
||||
decodeMetadataCodedIndex,
|
||||
readMetadataBlob,
|
||||
readMetadataString,
|
||||
} from "./ManagedMetadataHeaps.js";
|
||||
@@ -172,21 +171,21 @@ export const parseExportedTypes = (
|
||||
layout.codedIndexSize("Implementation"),
|
||||
);
|
||||
const token = metadataToken(39, row);
|
||||
const implementationReason = metadataCodedTokenInvalidReason(
|
||||
const implementation = decodeMetadataCodedIndex(
|
||||
implementationRaw,
|
||||
2,
|
||||
[38, 35, 39],
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (implementationReason !== null || implementationRaw === 0)
|
||||
if (implementation.kind !== "valid")
|
||||
issues.push({
|
||||
code: "invalid-row",
|
||||
scope: `metadata.ExportedType:${token}`,
|
||||
offset: implementationOffset,
|
||||
detail:
|
||||
implementationReason === null
|
||||
implementation.kind === "null"
|
||||
? "ExportedType implementation coded index 0x0 is null, but the Implementation column must reference a row"
|
||||
: `ExportedType implementation coded index 0x${implementationRaw.toString(16)} is invalid: ${implementationReason}`,
|
||||
: `ExportedType implementation coded index 0x${implementationRaw.toString(16)} is invalid: ${implementation.reason}`,
|
||||
});
|
||||
exportedTypes.push({
|
||||
token,
|
||||
@@ -196,12 +195,8 @@ export const parseExportedTypes = (
|
||||
flags,
|
||||
forwarder: (flags & TYPE_FORWARDER_FLAG) !== 0,
|
||||
type_def_id: typeDefId,
|
||||
implementation_token: metadataCodedToken(
|
||||
implementationRaw,
|
||||
2,
|
||||
[38, 35, 39],
|
||||
layout.rowCounts,
|
||||
),
|
||||
implementation_token:
|
||||
implementation.kind === "valid" ? implementation.token : null,
|
||||
});
|
||||
}, issues);
|
||||
}
|
||||
@@ -241,28 +236,23 @@ export const parseMemberRefs = (
|
||||
layout.blob?.size ?? 0,
|
||||
);
|
||||
const token = metadataToken(10, row);
|
||||
const parentReason = metadataCodedTokenInvalidReason(
|
||||
const parent = decodeMetadataCodedIndex(
|
||||
parentRaw,
|
||||
3,
|
||||
[2, 1, 26, 6, 27],
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (parentReason !== null || parentRaw === 0)
|
||||
if (parent.kind !== "valid")
|
||||
issues.push({
|
||||
code: "invalid-row",
|
||||
scope: `metadata.MemberRef:${token}`,
|
||||
offset: cursor.start,
|
||||
detail:
|
||||
parentReason === null
|
||||
parent.kind === "null"
|
||||
? "MemberRef parent coded index 0x0 is null, but the Class column must reference a row"
|
||||
: `MemberRef parent coded index 0x${parentRaw.toString(16)} is invalid: ${parentReason}`,
|
||||
: `MemberRef parent coded index 0x${parentRaw.toString(16)} is invalid: ${parent.reason}`,
|
||||
});
|
||||
const parentToken = metadataCodedToken(
|
||||
parentRaw,
|
||||
3,
|
||||
[2, 1, 26, 6, 27],
|
||||
layout.rowCounts,
|
||||
);
|
||||
const parentToken = parent.kind === "valid" ? parent.token : null;
|
||||
const parentName = typeName(parentToken);
|
||||
const decoded = budgetedSignature(
|
||||
sig,
|
||||
|
||||
@@ -392,37 +392,53 @@ export const metadataRowCursor = (
|
||||
);
|
||||
};
|
||||
|
||||
/** Decode a metadata coded index without signed 32-bit coercion. */
|
||||
export const metadataCodedToken = (
|
||||
raw: number,
|
||||
bits: number,
|
||||
tables: readonly (number | undefined)[],
|
||||
rowCounts: readonly number[],
|
||||
): string | null => {
|
||||
if (raw === 0) return null;
|
||||
const tag = raw & (2 ** bits - 1);
|
||||
const row = Math.floor(raw / 2 ** bits);
|
||||
const table = tables[tag];
|
||||
return table === undefined || row === 0 || row > (rowCounts[table] ?? 0)
|
||||
? null
|
||||
: metadataToken(table, row);
|
||||
};
|
||||
export type ManagedCodedIndex =
|
||||
| { readonly kind: "null" }
|
||||
| {
|
||||
readonly kind: "valid";
|
||||
readonly token: string;
|
||||
readonly table: number;
|
||||
readonly row: number;
|
||||
}
|
||||
| {
|
||||
readonly kind: "invalid";
|
||||
readonly reason: string;
|
||||
readonly table: number | undefined;
|
||||
readonly row: number;
|
||||
};
|
||||
|
||||
/** Explain why a non-null coded index cannot name an admitted metadata row. */
|
||||
export const metadataCodedTokenInvalidReason = (
|
||||
/** Decode and validate a metadata coded index without signed row coercion. */
|
||||
export const decodeMetadataCodedIndex = (
|
||||
raw: number,
|
||||
bits: number,
|
||||
tables: readonly (number | undefined)[],
|
||||
rowCounts: readonly number[],
|
||||
): string | null => {
|
||||
if (raw === 0) return null;
|
||||
): ManagedCodedIndex => {
|
||||
if (raw === 0) return { kind: "null" };
|
||||
const tag = raw & (2 ** bits - 1);
|
||||
const row = Math.floor(raw / 2 ** bits);
|
||||
const table = tables[tag];
|
||||
if (table === undefined) return `coded index tag ${tag} is not defined`;
|
||||
if (row === 0) return `coded index selects row zero in table ${table}`;
|
||||
if (table === undefined)
|
||||
return {
|
||||
kind: "invalid",
|
||||
reason: `coded index tag ${tag} is not defined`,
|
||||
table,
|
||||
row,
|
||||
};
|
||||
if (row === 0)
|
||||
return {
|
||||
kind: "invalid",
|
||||
reason: `coded index selects row zero in table ${table}`,
|
||||
table,
|
||||
row,
|
||||
};
|
||||
const count = rowCounts[table] ?? 0;
|
||||
return row > count
|
||||
? `coded index selects row ${row} in table ${table}, which has ${count} rows`
|
||||
: null;
|
||||
if (row > count)
|
||||
return {
|
||||
kind: "invalid",
|
||||
reason: `coded index selects row ${row} in table ${table}, which has ${count} rows`,
|
||||
table,
|
||||
row,
|
||||
};
|
||||
return { kind: "valid", token: metadataToken(table, row), table, row };
|
||||
};
|
||||
|
||||
@@ -16,8 +16,8 @@ import {
|
||||
} from "./ManagedMetadataLayout.js";
|
||||
import {
|
||||
metadataRowCursor,
|
||||
metadataCodedToken,
|
||||
metadataCodedTokenInvalidReason,
|
||||
decodeMetadataCodedIndex,
|
||||
type ManagedCodedIndex,
|
||||
metadataToken,
|
||||
readMetadataBlob,
|
||||
readMetadataGuid,
|
||||
@@ -404,29 +404,28 @@ const declaringTypeForMethod = (
|
||||
const attributeTypeName = (
|
||||
bytes: Buffer,
|
||||
layout: ManagedMetadataLayout,
|
||||
rawType: number,
|
||||
constructor: Extract<ManagedCodedIndex, { readonly kind: "valid" }>,
|
||||
memberLists: TypeMemberListFacts,
|
||||
typeName: ManagedMetadataTypeNameLookup,
|
||||
): string | null => {
|
||||
const tag = rawType & 7;
|
||||
const row = Math.floor(rawType / 8);
|
||||
if (row === 0) return null;
|
||||
if (tag === 2)
|
||||
const { table, row } = constructor;
|
||||
if (table === 6)
|
||||
return declaringTypeForMethod(bytes, layout, row, memberLists, typeName);
|
||||
if (tag !== 3) return null;
|
||||
const memberRefs = layout.table(10);
|
||||
if (memberRefs === undefined || row > memberRefs.rowCount) return null;
|
||||
const cursor = metadataRowCursor(bytes, layout, 10, row);
|
||||
const parent = cursor.readIndex(layout.codedIndexSize("MemberRefParent"));
|
||||
const parentTag = parent & 7;
|
||||
const parentRow = Math.floor(parent / 8);
|
||||
if (parentTag === 0) return typeName(2, parentRow);
|
||||
if (parentTag === 1) return typeName(1, parentRow);
|
||||
if (parentTag === 3)
|
||||
const parent = decodeMetadataCodedIndex(
|
||||
cursor.readIndex(layout.codedIndexSize("MemberRefParent")),
|
||||
3,
|
||||
[2, 1, 26, 6, 27],
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (parent.kind === "null") return null;
|
||||
if (parent.table === 2 || parent.table === 1)
|
||||
return typeName(parent.table, parent.row);
|
||||
if (parent.table === 6)
|
||||
return declaringTypeForMethod(
|
||||
bytes,
|
||||
layout,
|
||||
parentRow,
|
||||
parent.row,
|
||||
memberLists,
|
||||
typeName,
|
||||
);
|
||||
@@ -510,59 +509,48 @@ export const readCustomAttribute = (
|
||||
cursor.readIndex(layout.blobIndexSize),
|
||||
heapExtent,
|
||||
);
|
||||
const parent = metadataCodedToken(
|
||||
const parent = decodeMetadataCodedIndex(
|
||||
parentRaw,
|
||||
5,
|
||||
HAS_CUSTOM_ATTRIBUTE_TABLES,
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (parent === null) {
|
||||
const parentReason = metadataCodedTokenInvalidReason(
|
||||
parentRaw,
|
||||
5,
|
||||
HAS_CUSTOM_ATTRIBUTE_TABLES,
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (parent.kind !== "valid") {
|
||||
throw managedFailure(
|
||||
"invalid-row",
|
||||
`metadata.CustomAttribute:${metadataToken(12, row)}`,
|
||||
`CustomAttribute parent coded index 0x${parentRaw.toString(16)} is invalid${parentReason === null ? " because the required parent is null" : `: ${parentReason}`}`,
|
||||
`CustomAttribute parent coded index 0x${parentRaw.toString(16)} is invalid${parent.kind === "null" ? " because the required parent is null" : `: ${parent.reason}`}`,
|
||||
cursor.start,
|
||||
);
|
||||
}
|
||||
const constructor = metadataCodedToken(
|
||||
const constructor = decodeMetadataCodedIndex(
|
||||
typeRaw,
|
||||
3,
|
||||
CUSTOM_ATTRIBUTE_TYPE_TABLES,
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (constructor === null) {
|
||||
const typeReason = metadataCodedTokenInvalidReason(
|
||||
typeRaw,
|
||||
3,
|
||||
CUSTOM_ATTRIBUTE_TYPE_TABLES,
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (constructor.kind !== "valid") {
|
||||
issues.push({
|
||||
code: "invalid-row",
|
||||
scope: `metadata.CustomAttribute:${metadataToken(12, row)}`,
|
||||
offset: typeOffset,
|
||||
detail:
|
||||
typeReason === null
|
||||
constructor.kind === "null"
|
||||
? "CustomAttribute constructor coded index 0x0 is null, but Type must reference a MethodDef or MemberRef row"
|
||||
: `CustomAttribute constructor coded index 0x${typeRaw.toString(16)} is invalid: ${typeReason}`,
|
||||
: `CustomAttribute constructor coded index 0x${typeRaw.toString(16)} is invalid: ${constructor.reason}`,
|
||||
});
|
||||
}
|
||||
let typeName: string | null = null;
|
||||
if (constructor !== null)
|
||||
if (constructor.kind === "valid")
|
||||
typeName =
|
||||
readManagedValue(
|
||||
() => attributeTypeName(bytes, layout, typeRaw, memberLists, typeNames),
|
||||
() =>
|
||||
attributeTypeName(bytes, layout, constructor, memberLists, typeNames),
|
||||
issues,
|
||||
) ?? null;
|
||||
return {
|
||||
parent_token: parent,
|
||||
constructor_token: constructor,
|
||||
parent_token: parent.token,
|
||||
constructor_token: constructor.kind === "valid" ? constructor.token : null,
|
||||
type_name: typeName,
|
||||
value_length: value.length,
|
||||
value_sha256: managedDecodeBudget(layout).digest(value, "sha256"),
|
||||
@@ -603,28 +591,22 @@ export const readResource = ({
|
||||
const implementationRaw = cursor.readIndex(
|
||||
layout.codedIndexSize("Implementation"),
|
||||
);
|
||||
const implementationToken = metadataCodedToken(
|
||||
const implementation = decodeMetadataCodedIndex(
|
||||
implementationRaw,
|
||||
2,
|
||||
[38, 35, 39],
|
||||
layout.rowCounts,
|
||||
);
|
||||
const implementationReason = metadataCodedTokenInvalidReason(
|
||||
implementationRaw,
|
||||
2,
|
||||
[38, 35, 39],
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (implementationReason !== null)
|
||||
if (implementation.kind === "invalid")
|
||||
issues.push({
|
||||
code: "invalid-row",
|
||||
scope: `metadata.ManifestResource:${metadataToken(40, row)}`,
|
||||
offset: implementationOffset,
|
||||
detail: `ManifestResource implementation coded index 0x${implementationRaw.toString(16)} is invalid: ${implementationReason}`,
|
||||
detail: `ManifestResource implementation coded index 0x${implementationRaw.toString(16)} is invalid: ${implementation.reason}`,
|
||||
});
|
||||
let dataLength: number | null = null;
|
||||
let dataSha256: string | null = null;
|
||||
if (implementationRaw === 0) {
|
||||
if (implementation.kind === "null") {
|
||||
if (directory === null || declaredOffset > directory.size - 4) {
|
||||
issues.push({
|
||||
code: "invalid-resource",
|
||||
@@ -655,8 +637,9 @@ export const readResource = ({
|
||||
flags,
|
||||
visibility:
|
||||
(flags & 7) === 1 ? "public" : (flags & 7) === 2 ? "private" : "unknown",
|
||||
implementation_token: implementationToken,
|
||||
embedded: implementationRaw === 0,
|
||||
implementation_token:
|
||||
implementation.kind === "valid" ? implementation.token : null,
|
||||
embedded: implementation.kind === "null",
|
||||
declared_offset: declaredOffset,
|
||||
data_length: dataLength,
|
||||
data_sha256: dataSha256,
|
||||
|
||||
@@ -9,8 +9,7 @@ import {
|
||||
} from "./ManagedMemberInspectorCore.js";
|
||||
import type { ManagedMetadataLayout } from "./ManagedMetadataLayout.js";
|
||||
import {
|
||||
metadataCodedToken,
|
||||
metadataCodedTokenInvalidReason,
|
||||
decodeMetadataCodedIndex,
|
||||
metadataRowCursor,
|
||||
readMetadataBlob,
|
||||
} from "./ManagedMetadataHeaps.js";
|
||||
@@ -84,23 +83,23 @@ const readMethodSpec = (
|
||||
);
|
||||
const methodRaw = cursor.readIndex(layout.codedIndexSize("MethodDefOrRef"));
|
||||
const instantiation = cursor.readIndex(layout.blobIndexSize);
|
||||
const methodReason = metadataCodedTokenInvalidReason(
|
||||
const method = decodeMetadataCodedIndex(
|
||||
methodRaw,
|
||||
1,
|
||||
[6, 10],
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (methodReason !== null || methodRaw === 0)
|
||||
if (method.kind !== "valid")
|
||||
issues.push({
|
||||
code: "invalid-row",
|
||||
scope,
|
||||
offset: cursor.start,
|
||||
detail:
|
||||
methodReason === null
|
||||
method.kind === "null"
|
||||
? "MethodSpec method coded index 0x0 is null, but the Method column must reference a row"
|
||||
: `MethodSpec method coded index 0x${methodRaw.toString(16)} is invalid: ${methodReason}`,
|
||||
: `MethodSpec method coded index 0x${methodRaw.toString(16)} is invalid: ${method.reason}`,
|
||||
});
|
||||
const generic = metadataCodedToken(methodRaw, 1, [6, 10], layout.rowCounts);
|
||||
const generic = method.kind === "valid" ? method.token : null;
|
||||
const target = generic === null ? undefined : members.get(generic);
|
||||
// A MethodSpec instantiates a method, so a field target never lends it a
|
||||
// name, even to a field operand.
|
||||
|
||||
@@ -11,8 +11,7 @@ import {
|
||||
import type { ManagedMetadataLayout } from "./ManagedMetadataLayout.js";
|
||||
import {
|
||||
metadataRowCursor,
|
||||
metadataCodedToken,
|
||||
metadataCodedTokenInvalidReason,
|
||||
decodeMetadataCodedIndex,
|
||||
metadataToken,
|
||||
readMetadataString,
|
||||
} from "./ManagedMetadataHeaps.js";
|
||||
@@ -169,28 +168,24 @@ export const parseImplMaps = ({
|
||||
const memberForwardedRaw = cursor.readIndex(
|
||||
layout.codedIndexSize("MemberForwarded"),
|
||||
);
|
||||
const memberForwardedReason = metadataCodedTokenInvalidReason(
|
||||
const memberForwarded = decodeMetadataCodedIndex(
|
||||
memberForwardedRaw,
|
||||
1,
|
||||
[4, 6],
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (memberForwardedReason !== null || memberForwardedRaw === 0)
|
||||
if (memberForwarded.kind !== "valid")
|
||||
issues.push({
|
||||
code: "invalid-row",
|
||||
scope: `metadata.ImplMap:${metadataToken(28, row)}`,
|
||||
offset: memberForwardedOffset,
|
||||
detail:
|
||||
memberForwardedReason === null
|
||||
memberForwarded.kind === "null"
|
||||
? "ImplMap MemberForwarded coded index 0x0 is null, but the column must reference a Field or MethodDef row"
|
||||
: `ImplMap MemberForwarded coded index 0x${memberForwardedRaw.toString(16)} is invalid: ${memberForwardedReason}`,
|
||||
: `ImplMap MemberForwarded coded index 0x${memberForwardedRaw.toString(16)} is invalid: ${memberForwarded.reason}`,
|
||||
});
|
||||
const memberToken = metadataCodedToken(
|
||||
memberForwardedRaw,
|
||||
1,
|
||||
[4, 6],
|
||||
layout.rowCounts,
|
||||
);
|
||||
const memberToken =
|
||||
memberForwarded.kind === "valid" ? memberForwarded.token : null;
|
||||
const importName = readMetadataString(
|
||||
bytes,
|
||||
layout,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { managedDecodeBudget } from "./ManagedDecodeBudget.js";
|
||||
import type { ManagedMetadataLayout } from "./ManagedMetadataLayout.js";
|
||||
import {
|
||||
metadataCodedTokenInvalidReason,
|
||||
decodeMetadataCodedIndex,
|
||||
metadataRowCursor,
|
||||
metadataToken,
|
||||
readMetadataString,
|
||||
@@ -46,25 +46,25 @@ export const managedTypeReferences = (
|
||||
readManagedValue(() => {
|
||||
const cursor = metadataRowCursor(bytes, layout, 1, row);
|
||||
const raw = cursor.readIndex(layout.codedIndexSize("ResolutionScope"));
|
||||
const reason = metadataCodedTokenInvalidReason(
|
||||
const scope = decodeMetadataCodedIndex(
|
||||
raw,
|
||||
2,
|
||||
[0, 26, 35, 1],
|
||||
layout.rowCounts,
|
||||
);
|
||||
if (reason !== null)
|
||||
if (scope.kind === "invalid")
|
||||
issues.push({
|
||||
code: "invalid-row",
|
||||
scope: `metadata.TypeRef:${metadataToken(1, row)}`,
|
||||
offset: cursor.start,
|
||||
detail: `TypeRef ResolutionScope coded index 0x${raw.toString(16)} is invalid: ${reason}`,
|
||||
detail: `TypeRef ResolutionScope coded index 0x${raw.toString(16)} is invalid: ${scope.reason}`,
|
||||
});
|
||||
return {
|
||||
nameIndex: cursor.readIndex(layout.stringIndexSize),
|
||||
namespaceIndex: cursor.readIndex(layout.stringIndexSize),
|
||||
enclosingRow:
|
||||
raw !== 0 && (raw & 3) === 3 ? Math.floor(raw / 4) : null,
|
||||
scopeValid: reason === null,
|
||||
scope.kind !== "null" && scope.table === 1 ? scope.row : null,
|
||||
scopeValid: scope.kind !== "invalid",
|
||||
};
|
||||
}, issues) ?? null;
|
||||
rows.set(row, value);
|
||||
|
||||
Reference in New Issue
Block a user