From 3d4aeddb82d94f78692c4fe125bee413c0dec9e3 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Sat, 24 Oct 2026 17:03:27 -0500 Subject: [PATCH] Keep the reply scanner in a constant object A native build of a program that installs the C scanner but decodes no reply, such as the self-hosted source parser and CommonJS checks, failed at startup with "Cannot access 'valueEnd' before initialization": the build left the module variable uninitialized because its only other use was the assignment in useNativeJsonValueEnd, which checks it first. The scanner is now a property of a constant object that the decoders and useNativeJsonValueEnd read, so it is initialized in every program. --- .../compiler/src/frontend/ts7/semantic-json.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/compiler/src/frontend/ts7/semantic-json.ts b/packages/compiler/src/frontend/ts7/semantic-json.ts index 1a4d93bd6..8913007d3 100644 --- a/packages/compiler/src/frontend/ts7/semantic-json.ts +++ b/packages/compiler/src/frontend/ts7/semantic-json.ts @@ -104,15 +104,20 @@ export function jsonValueEnd(bytes: Uint8Array, start: number): number { } /** The scanner the decoders find value ends with: jsonValueEnd, or the - * native compiler's scanner (useNativeJsonValueEnd). */ -let valueEnd: (bytes: Uint8Array, start: number) => number = jsonValueEnd; + * native compiler's scanner (useNativeJsonValueEnd). It is a property of a + * constant object: a program that installs the scanner but decodes no + * reply must still initialize it, and an assigned module variable with no + * other reader would be left uninitialized by a native build. */ +const scanner: { valueEnd: (bytes: Uint8Array, start: number) => number } = { + valueEnd: jsonValueEnd, +}; /** Has the decoders find value ends with `scan`, which returns what * jsonValueEnd returns, or -1 where jsonValueEnd throws. The native compiler * installs its C scanner (native/ts7-process.c), which skips the strings that * make up most reply bytes with memchr. */ export function useNativeJsonValueEnd(scan: (bytes: Uint8Array, start: number) => number): void { - valueEnd = (bytes, start) => { + scanner.valueEnd = (bytes, start) => { const end = scan(bytes, start); if (end < 0) malformedReply(); return end; @@ -174,7 +179,7 @@ export function decodeSemanticObject( ): R | null { const start = skipJsonSpace(bytes, 0); if (start === bytes.length) return null; - const end = valueEnd(bytes, start); + const end = scanner.valueEnd(bytes, start); if (skipJsonSpace(bytes, end) !== bytes.length) malformedReply(); return replyElement(bytes, start, end, cached, build); } @@ -190,7 +195,7 @@ export function decodeSemanticObjects( let index = skipJsonSpace(bytes, 0); if (index === bytes.length) return null; if (bytes[index] !== OPEN_BRACKET) { - const end = valueEnd(bytes, index); + const end = scanner.valueEnd(bytes, index); if ( skipJsonSpace(bytes, end) !== bytes.length || parseJsonSlice(bytes, index, end) !== null @@ -205,7 +210,7 @@ export function decodeSemanticObjects( return result; } for (;;) { - const end = valueEnd(bytes, index); + const end = scanner.valueEnd(bytes, index); if (end === index) malformedReply(); result.push(replyElement(bytes, index, end, cached, build)); index = skipJsonSpace(bytes, end);