fix(managed): decode the CIL no. prefix (#2241)

The CIL opcode table held five of the six ECMA-335 prefix opcodes and
omitted no. (FE 19 <unsigned int8>, III.2.2). A method body using it was
reported as malformed with "Unsupported CIL opcode 0xfe19", losing its
instruction counts and normalized IL digest.

Decode it with its one-byte check mask and treat it as a prefix, so it
shares an instruction boundary with the opcode it modifies.


Claude-Session: https://claude.ai/code/session_012pRGskbPGjT444CgJfuhhj

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Angelo Perez 2026-10-13 09:16:00 -04:00 committed by GitHub
parent ece15ed6a1
commit e46bed73f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 27 additions and 0 deletions

View File

@ -345,6 +345,31 @@ describe("managed exception region prefix boundaries", () => {
});
});
it("decodes the no. prefix and keeps it with its opcode", () => {
// ldarg.0; no. nullcheck; ldfld 0x04000001; pop; ret
const il = Buffer.from([2, 0xfe, 0x19, 4, 0x7b, 1, 0, 0, 4, 0x26, 0x2a]);
expect(
managedBodyWithRegion({ il, tryLength: 10, handlerOffset: 10 }),
).toMatchObject({
status: "present",
issue: null,
decoded_instruction_count: 5,
opcode_counts: { "ldarg.0": 1, "no.": 1, ldfld: 1, pop: 1, ret: 1 },
});
expect(
managedBodyWithRegion({
il,
tryOffset: 4,
tryLength: 5,
handlerOffset: 10,
}),
).toMatchObject({
status: "malformed",
issue:
"Exception clause try range does not align with CIL instruction boundaries",
});
});
it.each([
["start", { tryOffset: 5, tryLength: 1, handlerOffset: 6 }],
[

View File

@ -12,6 +12,7 @@ const PREFIX_OPCODES = new Set([
"tail.",
"constrained.",
"readonly.",
"no.",
]);
const OPCODES = new Map<
@ -232,6 +233,7 @@ const OPCODES = new Map<
[0xfe16, { name: "constrained.", operand: "type" }],
[0xfe17, { name: "cpblk", operand: "none" }],
[0xfe18, { name: "initblk", operand: "none" }],
[0xfe19, { name: "no.", operand: "short-i" }],
[0xfe1a, { name: "rethrow", operand: "none" }],
[0xfe1c, { name: "sizeof", operand: "type" }],
[0xfe1d, { name: "refanytype", operand: "none" }],