refactor(go): parse module records through one byte contract

Parse the original unframed module bytes for every build-info result. Remove the duplicate text facade and string-source branch while retaining observed text, framed bytes, malformed lines, quoting and replacement semantics.

Validation: 163 Go parser, image, provider and public CLI/MCP tests passed using local binary fixtures; build, static and documentation checks passed.
This commit is contained in:
morluto committed 2026-10-25 02:44:32 +08:00
1 parent faf219e90b
commit 31fc9762a3
3 files changed
+24 -41

No files matched your search

+2 -5
View File
@@ -11,7 +11,7 @@ import {
} from "../native/metadata/BinaryContainer.js";
import { readBinaryContainer } from "../native/metadata/readBinaryContainer.js";
import type { GoModuleMetadata } from "../domain/go/goBinary.js";
import { parseGoModuleBytes, parseGoModuleText } from "./GoModuleText.js";
import { parseGoModuleBytes } from "./GoModuleText.js";
import {
goBuildInfoSearch,
type GoBuildInfoSearchRange,
@@ -212,10 +212,7 @@ export const readGoBinaryImage = (bytes: Buffer): GoBinaryImage => {
module_bytes_base64: module.bytes.toString("base64"),
version_location: version.location,
module_location: module.location,
module:
moduleText === null
? parseGoModuleBytes(moduleBody)
: parseGoModuleText(moduleText),
module: parseGoModuleBytes(moduleBody),
},
...(sectionNamesLimitation === undefined
? {}
+9 -24
View File
@@ -117,25 +117,17 @@ interface ModuleSourceLine {
readonly terminated: boolean;
}
function* moduleSourceLines(
source: string | Buffer,
): Generator<ModuleSourceLine> {
function* moduleSourceLines(source: Buffer): Generator<ModuleSourceLine> {
let start = 0;
while (start < source.length) {
const newline =
typeof source === "string"
? source.indexOf("\n", start)
: source.indexOf(10, start);
const newline = source.indexOf(10, start);
const end = newline < 0 ? source.length : newline;
let line: string | Buffer;
if (typeof source === "string") line = source.slice(start, end);
else {
const bytes = source.subarray(start, end);
try {
line = utf8.decode(bytes);
} catch {
line = bytes;
}
const bytes = source.subarray(start, end);
try {
line = utf8.decode(bytes);
} catch {
line = bytes;
}
yield { line, terminated: newline >= 0 };
if (newline < 0) return;
@@ -147,7 +139,8 @@ const modulePrefixes = ["mod\t", "dep\t", "=>\t"].map((prefix) =>
Buffer.from(prefix),
);
const parseModuleRecords = (source: string | Buffer): GoModuleMetadata => {
/** Decode an unframed Go module body while retaining non-UTF-8 line bytes exactly. */
export const parseGoModuleBytes = (source: Buffer): GoModuleMetadata => {
let path: string | null = null;
let main: GoModule | null = null;
const dependencies: GoModule[] = [];
@@ -216,11 +209,3 @@ const parseModuleRecords = (source: string | Buffer): GoModuleMetadata => {
complete: unparsed_lines.length + unparsed_line_bytes_base64.length === 0,
};
};
/** Decode Go's module records while preserving unsupported/malformed source lines. */
export const parseGoModuleText = (text: string): GoModuleMetadata =>
parseModuleRecords(text);
/** Decode an unframed Go module body while retaining non-UTF-8 line bytes exactly. */
export const parseGoModuleBytes = (bytes: Buffer): GoModuleMetadata =>
parseModuleRecords(bytes);
+13 -12
View File
@@ -1,13 +1,12 @@
import { describe, expect, it } from "vitest";
import {
parseGoModuleBytes,
parseGoModuleText,
} from "../../../src/go/GoModuleText.js";
import { parseGoModuleBytes } from "../../../src/go/GoModuleText.js";
describe("Go producer module text", () => {
it("preserves local replacements, empty fields and ordered duplicate build settings", () => {
const parsed = parseGoModuleText(
"path\texample.invalid/app\nmod\texample.invalid/app\t(devel)\t\ndep\texample.invalid/lib\tv1.2.3\th1:bytes\n=>\t../local lib\t\t\nbuild\tGOOS=linux\nbuild\tGOOS=custom\nbuild\t__proto__=kept\n",
const parsed = parseGoModuleBytes(
Buffer.from(
"path\texample.invalid/app\nmod\texample.invalid/app\t(devel)\t\ndep\texample.invalid/lib\tv1.2.3\th1:bytes\n=>\t../local lib\t\t\nbuild\tGOOS=linux\nbuild\tGOOS=custom\nbuild\t__proto__=kept\n",
),
);
expect(parsed).toMatchObject({
path: "example.invalid/app",
@@ -39,7 +38,7 @@ describe("Go producer module text", () => {
) +
"\n" +
"build\traw=`a\rb`\n";
expect(parseGoModuleText(text)).toMatchObject({
expect(parseGoModuleBytes(Buffer.from(text))).toMatchObject({
settings: [
{ key: "key=quoted", value: "\x07\x0béé☺🚀" },
{ key: "raw", value: "ab" },
@@ -51,7 +50,7 @@ describe("Go producer module text", () => {
it("reports unparsed records and malformed escaped bytes while retaining observed fields", () => {
const text =
'path\tapp\nmod\tapp\t(devel)\nbuild\tbad="\\xff"\nfuture\tdata\nbuild\tGOARCH=amd64';
expect(parseGoModuleText(text)).toMatchObject({
expect(parseGoModuleBytes(Buffer.from(text))).toMatchObject({
path: "app",
main: { sum: null },
settings: [],
@@ -65,8 +64,8 @@ describe("Go producer module text", () => {
});
it("does not attach a replacement after a malformed module declaration", () => {
const parsed = parseGoModuleText(
"dep\tfirst\tv1\ndep\tmalformed\n=>\tlocal\t\t\n",
const parsed = parseGoModuleBytes(
Buffer.from("dep\tfirst\tv1\ndep\tmalformed\n=>\tlocal\t\t\n"),
);
expect(parsed.dependencies).toEqual([
{ path: "first", version: "v1", sum: null, replacement: null },
@@ -76,8 +75,10 @@ describe("Go producer module text", () => {
it("preserves duplicate singleton declarations as unknown instead of silently overwriting", () => {
expect(
parseGoModuleText(
"path\tfirst\npath\tsecond\nmod\tfirst\tv1\nmod\tsecond\tv2\n",
parseGoModuleBytes(
Buffer.from(
"path\tfirst\npath\tsecond\nmod\tfirst\tv1\nmod\tsecond\tv2\n",
),
),
).toMatchObject({
path: "first",