Files
rea/tests/fixtures/javaClassFile.ts
T
morluto 921bb95755 fix(artifacts): distinguish Java classes from universal Mach-O
Classify FAT containers using shared architecture, range, and observed slice-header facts rather than the CAFEBABE signature alone. Preserve typed unsupported and malformed reasons, reject malformed sibling slices consistently, and remove repeated range parsing in consumers.

Validation: valid small and large JVM classes in public JAR/ZIP inspection, four FAT width/byte-order controls, and focused executable/Mach-O boundary tests. The class fixtures were checked with JDK javap; no new JVM analysis capability is advertised.

The Apple selection and Hopper readers retain their existing parsing because they also own oversized-count diagnostics, reserved fields, streaming final-size validation, or source freshness that the shared header result does not represent.

Representative public mixed-ZIP inventory probe, four fresh processes with an already warm filesystem cache: first process elapsed 626.8 -> 548.3 ms, CPU 740 -> 740 ms, sampled family peak RSS 375.5 -> 345.4 MiB. Subsequent three-process medians: elapsed 767.2 -> 454.0 ms, CPU 890 -> 640 ms, peak RSS 368.7 -> 346.2 MiB. Estimated output tokens 2310 -> 2292. Concurrent load was uncontrolled, so these observations do not establish a speed improvement. Whole-agent task tokens and schema/call overhead were not measured.

Fixes #2317
2026-10-13 04:12:04 +08:00

29 lines
1.0 KiB
TypeScript

/** Build a valid minimal JVM class file, optionally with an unused UTF-8 constant. */
export const javaClassFile = (extraUtf8Bytes = 0): Buffer => {
const utf8 = (value: string): Buffer => {
const bytes = Buffer.from(value, "utf8");
const entry = Buffer.alloc(3 + bytes.length);
entry[0] = 1;
entry.writeUInt16BE(bytes.length, 1);
bytes.copy(entry, 3);
return entry;
};
const constants = [
Buffer.from([7, 0, 2]),
utf8("Hello"),
Buffer.from([7, 0, 4]),
utf8("java/lang/Object"),
...(extraUtf8Bytes === 0 ? [] : [utf8("x".repeat(extraUtf8Bytes))]),
];
const headerAndPool = Buffer.alloc(10);
headerAndPool.writeUInt32BE(0xcafebabe, 0);
headerAndPool.writeUInt16BE(0, 4);
headerAndPool.writeUInt16BE(65, 6);
headerAndPool.writeUInt16BE(constants.length + 1, 8);
const classBody = Buffer.alloc(14);
[0x21, 1, 3, 0, 0, 0, 0].forEach((value, index) =>
classBody.writeUInt16BE(value, index * 2),
);
return Buffer.concat([headerAndPool, ...constants, classBody]);
};