fix(ingestion): two-channel binding lifecycle (closes #1066) + scope-resolution I8 hardening (#1082)
Some checks are pending
CI / quality (push) Waiting to run
CI / tests (push) Waiting to run
CI / e2e (push) Waiting to run
CI / scope-parity (push) Waiting to run
CI / Save PR Metadata (push) Blocked by required conditions
CI / CI Gate (push) Blocked by required conditions
Release Candidate / Check if release candidate should run (push) Waiting to run
Release Candidate / ci (push) Blocked by required conditions
Release Candidate / Publish release candidate to npm (push) Blocked by required conditions
Release Candidate / Build & Push RC Docker images (push) Blocked by required conditions

* fix(csharp): adaptive tree-sitter buffer + frozen-bucket clone for cross-namespace siblings (#1066)

Two coupled regressions surfaced when analyzing real-world C# repos with
large source files (issue #1066):

1. Tree-sitter `parser.parse()` is hard-coded to a 32 KB buffer by
   default. Any file exceeding that threshold throws `Invalid argument`
   on the worker re-parse path of `populateCsharpNamespaceSiblings`
   (and the analogous Python / TypeScript captures fallbacks).
2. After the buffer fix unblocks the AST walk, the hook tries to
   `push()` onto the inner `BindingRef[]` array fetched from
   `indexes.bindings` — but `materializeBindings` froze that array via
   `Object.freeze(refs.slice())`. Result: `Cannot add property N,
   object is not extensible`.

Fixes:

- `csharp/captures.ts`, `python/captures.ts`, `typescript/captures.ts`:
  pass `bufferSize: getTreeSitterBufferSize(sourceText.length)` to
  `parser.parse()` on the cache-miss path so multi-MB files parse.
- `csharp/namespace-siblings.ts`: introduce `cloneBindingBucket` to
  copy the frozen array before mutating, then `set()` the new array
  back. This is a working but architecturally compromised workaround
  (#1050 follow-up will replace it with an explicit augmentation
  channel — see docs/plans/2026-04-26-001 plan).

Tests:

- New `csharp-large-cache-miss-resolution` fixture (Models/Services/
  Other layout, ~77 KB padded UserService.cs) drives the buffer-size
  failure end-to-end through worker mode.
- `csharp.test.ts`: 4 new regression assertions covering both the
  parse-time buffer-size failure and the freeze workaround.
- Per-language captures unit tests gain "large cache-miss file uses
  adaptive buffer" coverage (TS, Python, C#).
- `csharp-hooks.test.ts`: in-memory freeze regression test that
  reproduces the `Cannot add property` crash without invoking the C#
  parser at all.

Made-with: Cursor

* refactor(scope-resolution): add bindingAugmentations channel to indexes

Step 1 of the binding-augmentation-channel refactor (issue #1066
follow-up). Pure shape change — no consumers yet.

Adds a new `readonly bindingAugmentations` field to
`ScopeResolutionIndexes` initialized as an empty `Map` by
`finalizeScopeModel`. The new channel is the dedicated post-finalize
write target for hooks like `populateCsharpNamespaceSiblings`, so
`indexes.bindings` can stay frozen and finalize-owned.

Behavior unchanged: nothing reads or writes the new field yet. tsc and
the full unit suite remain green.

Plan: docs/plans/2026-04-26-001-binding-augmentation-channel.md (local
only — `docs/plans/` is gitignored).

Made-with: Cursor

* feat(scope-resolution): add lookupBindingsAt dual-source helper

Step 2 of the binding-augmentation-channel refactor. Introduces a
single primitive every walker uses to read both the finalize-owned
`indexes.bindings` channel and the post-finalize
`indexes.bindingAugmentations` channel.

Contract:
- Finalized refs come first (preserves existing precedence).
- Augmented refs append, deduped by `def.nodeId`.
- Empty input on both channels returns a shared frozen empty array.
- Single-channel hits return the bucket by reference (no allocation).

No consumers are wired yet — Step 3 routes the existing walker
primitives through this helper. Augmentations remain empty for every
language; behavior of the full suite is unchanged.

8 unit tests pin precedence, dedup, identity for single-channel hits,
and the shared-empty-frozen-array sentinel.

Made-with: Cursor

* refactor(scope-resolution): route binding lookups through lookupBindingsAt

Step 3 of the binding-augmentation-channel refactor. Every direct
`indexes.bindings.get(...)` consumer in the post-finalize phase is
now routed through `lookupBindingsAt` (per-name) or `namesAtScope`
+ `lookupBindingsAt` (bulk iteration).

Routed sites:
- `findClassBindingInScope` (walkers.ts) — class-receiver lookups.
- `findCallableBindingInScope` (walkers.ts) — free-call lookups.
- `findExportedDefByName` (walkers.ts) — module-scope-fallback
  callable lookups.
- `propagateImportedReturnTypes` (passes/imported-return-types.ts)
  — bulk iteration over an importer's binding entries; switched to
  `namesAtScope` + per-name `lookupBindingsAt` so post-finalize
  augmentations are visible to import-derived typeBinding mirrors.

Behavior unchanged: augmentations are empty across the suite (Step 4
populates them for C# `populateNamespaceSiblings`). 587
scope-resolution unit tests + 50 integration resolver suites green
(4 pre-existing Swift method-implements failures unrelated to this
work).

Adds `namesAtScope` companion helper for the bulk-iteration callers.

Made-with: Cursor

* refactor(csharp): write namespace siblings to bindingAugmentations channel

Step 4 of the binding-augmentation-channel refactor. The C#
`populateNamespaceSiblings` hook is the only consumer that needed
to inject cross-file bindings post-finalize, and prior to this
change it cloned the (frozen) finalized `BindingRef[]` arrays
through a `cloneBindingBucket` helper, then `set()`-back the new
array — a workaround for the `Object.freeze` applied by
`finalize-algorithm.ts` (issue #1066 root cause).

Architecturally that violated `ScopeResolver` Invariant I8 (which
permits post-finalize modifications but not in-place mutation of
finalized buckets). It also forced read-side consumers to be aware
of the workaround.

This change:
* Switches the three C# write sites to append into
  `indexes.bindingAugmentations` via `getAugmentationBucket`. The
  augmentation channel was added in Step 1 and is mutable by
  contract: inner `BindingRef[]` arrays here are NEVER frozen.
* Deletes `cloneBindingBucket` and `getMutableScopeBindings`
  (workaround helpers no longer needed).
* `lookupBindingsAt` (Step 2) merges the two channels transparently
  for every walker (Step 3), so behavior is unchanged for callers.
* Updates the unit test to assert against both channels: finalized
  bucket stays frozen and untouched, cross-file siblings show up in
  augmentations only. Renamed the test accordingly.

Validation:
* `npx tsc --noEmit` clean.
* csharp hooks unit + walkers-augmentations unit + csharp integration
  resolver suite all green (236/236).
* Wider `test/unit/scope-resolution test/integration/resolvers`
  suite: 2507 pass, only 4 pre-existing Swift METHOD_IMPLEMENTS
  failures remain (unrelated to this work, present on baseline).

Refs: issue #1066, ADR-pending binding-augmentation-channel.
Made-with: Cursor

* feat(scope-resolution): tighten I8 + add validateBindingsImmutability dev guard

Step 5 of the binding-augmentation-channel refactor. Captures the
new two-channel binding lifecycle in the contract docs and adds a
dev-mode runtime validator so a future hook cannot silently drift
back into mutating `indexes.bindings`.

Contract changes:
* `contract/scope-resolver.ts` — rewrote Invariant I8 to describe
  the two channels (`indexes.bindings` is finalize-output and
  immutable post-finalize; `indexes.bindingAugmentations` is the
  append-only post-finalize channel populated by hooks like
  `populateNamespaceSiblings`). Documented `lookupBindingsAt` as
  the read-side merger and pointed at the new validator as the
  enforcement mechanism.
* `gitnexus-shared/src/scope-resolution/types.ts` — extended the
  module-header lifecycle contract to call out
  `bindingAugmentations` alongside `ReferenceIndex` as the two
  structures populated after the freeze.

Validator:
* New `pipeline/validate-bindings-immutability.ts` mirrors the
  shape of `validateOwnershipParity` (#909): runs only when
  `NODE_ENV !== 'production' && VALIDATE_SEMANTIC_MODEL !== '0'`,
  emits via `onWarn`, never throws. Asserts (a) every inner
  `BindingRef[]` in `indexes.bindings` is `Object.isFrozen`, and
  (b) every inner array in `indexes.bindingAugmentations` is NOT
  frozen.
* Wired into `pipeline/run.ts` after both
  `populateNamespaceSiblings` and `propagateImportedReturnTypes`,
  before `resolveReferenceSites`. One sweep covers the full
  post-finalize surface.

Tests:
* `validate-bindings-immutability.test.ts` — 6 cases pinning happy
  path, both drift directions, multi-violation accumulation, and
  both production no-op gates.

All scope-resolution + csharp resolver tests green (242/242 in the
focused run; matches the wider Step 4 baseline).

Made-with: Cursor

* fix(ingestion): size tree-sitter buffers from UTF-8 bytes

Tree-sitter buffer sizing is byte-based, so computing adaptive buffers from JavaScript string length under-sized UTF-8-heavy files. Make getTreeSitterBufferSize accept source text directly and compute Buffer.byteLength internally, then update all parse call sites and max-buffer skip checks to use byte length.

Add multibyte cache-miss and cap regressions for C#, Python, TypeScript, and the C# namespace-sibling fallback parse path.

Made-with: Cursor

* test(scope-resolution): pin augmentation read paths

Add focused unit coverage for augmented-only binding reads across the routed walker helpers and imported-return-type propagation path. Clarify I8 wording around lexical Scope.bindings versus post-finalize index channels, and document the intentional local-only behavior of findExportedDef.

Also switch the immutability validator tests to Vitest env stubs, document one intentional validator blind spot, and split C# namespace-sibling tests so UTF-8 parsing and augmentation-channel behavior are asserted independently.

Made-with: Cursor

* test(scope-resolution): avoid slow parser stress fixtures

Replace high-cardinality large-file capture fixtures with large padding plus a trailing declaration. This still proves adaptive tree-sitter buffers parse beyond large ASCII and UTF-8-heavy input, without making query matching process thousands of declarations and risking timeouts.

Made-with: Cursor

* test(scope-resolution): add python and typescript cache-miss resolver regressions

Add worker-mode resolver integration coverage mirroring the C# #1066 scenario for Python and TypeScript. Each test builds a temp fixture with large ASCII and UTF-8-heavy source padding, then asserts trailing declarations and call edges still resolve after scope-resolution cache-miss reparsing.

Made-with: Cursor

* refactor(scope-resolution): gate I8 validator and fast-path namesAtScope

Addresses SPARC reviewer feedback on the binding-augmentation channel:

- Validator gate is now opt-in outside development. Extract
  isSemanticModelValidatorEnabled() in utils/env.ts as the single
  predicate; both validateBindingsImmutability and phase.ts's warn
  handler share it. Default CLI runs no longer pay the O(binding-buckets)
  scan, and explicit VALIDATE_SEMANTIC_MODEL=1 now emits warnings even
  when NODE_ENV is unset.
- namesAtScope returns Iterable<string> and zero-allocates when at most
  one channel is populated (returns Map.keys() directly), only
  materializing a Set when both channels carry names. The caller-side
  branching and EMPTY_NAMES escape hatch in propagateImportedReturnTypes
  are gone -- both helpers handle the empty-augmentation case internally.
- C# namespace-siblings header/JSDoc, model JSDoc, I8 contract prose, and
  the #1066 integration-test header rewritten to say post-finalize fanout
  appends only to bindingAugmentations; finalized refs come first and win
  duplicate def.nodeId metadata; local lexical Scope.bindings remains the
  first-tier shadowing channel.

Validator unit-test setup deduplicated via beforeEach and extended with
default-CLI no-op + explicit-opt-in cases.

Made-with: Cursor
This commit is contained in:
Gergő Magyar 2026-04-26 12:16:09 +01:00 committed by GitHub
parent ab077b4c29
commit 98ee665889
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 1831 additions and 131 deletions

View file

@ -10,8 +10,16 @@
* Lifecycle contract (RFC §2.8): scopes are **constructed during extraction,
* linked during finalize, immutable after finalize**. All fields are
* `readonly` at the type level; `Object.freeze` is applied at runtime in dev
* builds. `ReferenceIndex` is the sole structure populated after freeze by
* resolution, before emission.
* builds.
*
* Two structures are populated after freeze:
* 1. `ReferenceIndex` by resolution, before emission.
* 2. `ScopeResolutionIndexes.bindingAugmentations` the dedicated
* append-only post-finalize binding channel (e.g. C# same-namespace
* cross-file fanout). The companion `indexes.bindings` is the
* finalize-output channel and is deep-frozen by `materializeBindings`;
* walkers consult both via `lookupBindingsAt`. See `ScopeResolver`
* Invariant I8 for the full lifecycle contract.
*/
import type { NodeLabel } from '../graph/types.js';

View file

@ -770,7 +770,7 @@ export const processCalls = async (
if (!tree) {
try {
tree = parser.parse(file.content, undefined, {
bufferSize: getTreeSitterBufferSize(file.content.length),
bufferSize: getTreeSitterBufferSize(file.content),
});
} catch (parseError) {
continue;
@ -3257,7 +3257,7 @@ export const extractFetchCallsFromFiles = async (
if (!tree) {
try {
tree = parser.parse(file.content, undefined, {
bufferSize: getTreeSitterBufferSize(file.content.length),
bufferSize: getTreeSitterBufferSize(file.content),
});
} catch {
continue;

View file

@ -1,3 +1,5 @@
import { Buffer } from 'node:buffer';
/**
* Default minimum buffer size for tree-sitter parsing (512 KB).
* tree-sitter requires bufferSize >= file size in bytes.
@ -12,8 +14,13 @@ export const TREE_SITTER_MAX_BUFFER = 32 * 1024 * 1024;
/**
* Compute adaptive buffer size for tree-sitter parsing.
* Uses 2× file size, clamped between 512 KB and 32 MB.
* Previous 256 KB fixed limit silently skipped files > ~200 KB (e.g., imgui.h at 411 KB).
* Uses 2x UTF-8 byte size, clamped between 512 KB and 32 MB.
* Keeps tree-sitter's byte-sized buffer above large ASCII and multibyte sources.
*/
export const getTreeSitterBufferSize = (contentLength: number): number =>
Math.min(Math.max(contentLength * 2, TREE_SITTER_BUFFER_SIZE), TREE_SITTER_MAX_BUFFER);
export const getTreeSitterContentByteLength = (sourceText: string): number =>
Buffer.byteLength(sourceText, 'utf8');
export const getTreeSitterBufferSize = (sourceText: string): number => {
const byteLength = getTreeSitterContentByteLength(sourceText);
return Math.min(Math.max(byteLength * 2, TREE_SITTER_BUFFER_SIZE), TREE_SITTER_MAX_BUFFER);
};

View file

@ -141,6 +141,11 @@ export function finalizeScopeModel(
methodDispatch,
imports: finalizeOut.imports,
bindings: finalizeOut.bindings,
// Empty post-finalize augmentation channel. Populated (if at all)
// by language hooks like `populateCsharpNamespaceSiblings` running
// AFTER `finalizeScopeModel` returns, before `resolveReferenceSites`
// consumes the bundle. Most languages leave it empty.
bindingAugmentations: new Map(),
referenceSites: Object.freeze([...allReferenceSites]),
sccs: finalizeOut.sccs,
stats: finalizeOut.stats,

View file

@ -220,7 +220,7 @@ export const processHeritage = async (
// Use larger bufferSize for files > 32KB
try {
tree = parser.parse(file.content, undefined, {
bufferSize: getTreeSitterBufferSize(file.content.length),
bufferSize: getTreeSitterBufferSize(file.content),
});
} catch (parseError) {
// Skip files that can't be parsed
@ -414,7 +414,7 @@ export async function extractExtractedHeritageFromFiles(
if (!tree) {
try {
tree = parser.parse(file.content, undefined, {
bufferSize: getTreeSitterBufferSize(file.content.length),
bufferSize: getTreeSitterBufferSize(file.content),
});
} catch {
continue;

View file

@ -306,7 +306,7 @@ export const processImports = async (
if (!tree) {
try {
tree = parser.parse(file.content, undefined, {
bufferSize: getTreeSitterBufferSize(file.content.length),
bufferSize: getTreeSitterBufferSize(file.content),
});
} catch (parseError) {
continue;

View file

@ -23,6 +23,7 @@ import { computeCsharpArityMetadata } from './arity-metadata.js';
import { synthesizeCsharpReceiverBinding } from './receiver-binding.js';
import { getCsharpParser, getCsharpScopeQuery } from './query.js';
import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
import { getTreeSitterBufferSize } from '../../constants.js';
/** Declaration anchors that carry function-like arity metadata. */
const FUNCTION_DECL_TAGS = [
@ -52,7 +53,9 @@ export function emitCsharpScopeCaptures(
// the LanguageProvider contract layer; cast here at the use site.
let tree = cachedTree as ReturnType<ReturnType<typeof getCsharpParser>['parse']> | undefined;
if (tree === undefined) {
tree = getCsharpParser().parse(sourceText);
tree = getCsharpParser().parse(sourceText, undefined, {
bufferSize: getTreeSitterBufferSize(sourceText),
});
recordCacheMiss();
} else {
recordCacheHit();

View file

@ -11,17 +11,18 @@
* field-chain resolution fails at `findClassBindingInScope('User')`
* in the Service.cs scope chain.
*
* Implementation: after the finalize pass populates `indexes.bindings`
* (from explicit `using` directives), walk each file's tree-sitter
* AST for `namespace_declaration` / `file_scoped_namespace_declaration`
* and `using_directive` nodes. The orchestrator hands us its
* `treeCache` so files already parsed by `extractParsedFile` are
* re-used instead of re-parsed `ParsedFile`'s underlying tree is
* the single source of truth. Group classes by namespace, and inject
* cross-file sibling classes into each Namespace scope's finalized
* bindings with `origin: 'namespace'` a tier below `local` so a
* local declaration still shadows a cross-file sibling with the same
* name.
* Implementation: after the finalize pass populates immutable
* `indexes.bindings` (from explicit `using` directives), walk each
* file's tree-sitter AST for `namespace_declaration` /
* `file_scoped_namespace_declaration` and `using_directive` nodes.
* The orchestrator hands us its `treeCache` so files already parsed
* by `extractParsedFile` are re-used instead of re-parsed
* `ParsedFile`'s underlying tree is the single source of truth.
* Group classes by namespace, and append cross-file sibling classes
* into each Namespace scope's `bindingAugmentations` bucket with
* `origin: 'namespace'`. Finalized bindings remain first in
* `lookupBindingsAt`, and local lexical `Scope.bindings` remains the
* first-tier shadowing channel.
*
* The tree-sitter walk is authoritative: it sees `global using static`,
* aliased `using static X = Y.Z;`, attributed namespace declarations,
@ -34,6 +35,7 @@ import type { SyntaxNode } from 'tree-sitter';
import type { BindingRef, ParsedFile, Scope, ScopeId, SymbolDefinition } from 'gitnexus-shared';
import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
import { getCsharpParser } from './query.js';
import { getTreeSitterBufferSize } from '../../constants.js';
interface CsharpFileStructure {
/** Declared namespace names in file source order. Empty array means
@ -52,7 +54,11 @@ interface CsharpFileStructure {
* shared across calls. */
function extractFileStructure(content: string, cachedTree: unknown): CsharpFileStructure {
type CsharpTree = ReturnType<ReturnType<typeof getCsharpParser>['parse']>;
const tree = (cachedTree as CsharpTree | undefined) ?? getCsharpParser().parse(content);
const tree =
(cachedTree as CsharpTree | undefined) ??
getCsharpParser().parse(content, undefined, {
bufferSize: getTreeSitterBufferSize(content),
});
const namespaces: string[] = [];
const usingStaticPaths: string[] = [];
@ -106,8 +112,8 @@ export interface CsharpSiblingInputs {
}
/**
* Mutate `indexes.bindings` in-place, adding cross-file sibling class
* defs to each Namespace scope. Class-like defs (Class / Interface /
* Append cross-file sibling class defs to each Namespace scope's
* `bindingAugmentations` bucket. Class-like defs (Class / Interface /
* Struct / Record / Enum) are visible cross-file; method / field
* members are not.
*/
@ -198,12 +204,15 @@ export function populateCsharpNamespaceSiblings(
}
}
// Inject cross-file siblings into each namespace scope's finalized
// bindings. `indexes.bindings` is typed `ReadonlyMap<ScopeId, ...>`
// but is a plain Map at runtime; mutating here is the established
// pattern (see `propagateImportedReturnTypes` which does the same
// for module-scope typeBindings).
const finalized = indexes.bindings as Map<ScopeId, Map<string, BindingRef[]>>;
// Inject cross-file siblings into each namespace scope's
// post-finalize augmentation channel (per I8). The
// `indexes.bindingAugmentations` map is the dedicated mutable
// append-only buffer for post-finalize hooks: inner `BindingRef[]`
// arrays here are NEVER frozen (unlike `indexes.bindings`, which
// `materializeBindings` freezes). Walkers consult both channels
// via `lookupBindingsAt`; we never need to consult or mutate
// `indexes.bindings`.
const augmentations = indexes.bindingAugmentations as Map<ScopeId, Map<string, BindingRef[]>>;
// Cross-namespace type-binding propagation: for each file, mirror
// method return-type bindings from same-namespace sibling files and
@ -301,17 +310,13 @@ export function populateCsharpNamespaceSiblings(
const simpleName = mq.includes('.') ? mq.slice(mq.lastIndexOf('.') + 1) : mq;
if (simpleName === '') continue;
// Add to `indexes.bindings[moduleScope]` so
// `findCallableBindingInScope` picks it up.
let scopeBindings = finalized.get(moduleScope.id);
if (scopeBindings === undefined) {
scopeBindings = new Map<string, BindingRef[]>();
finalized.set(moduleScope.id, scopeBindings);
}
const existing = scopeBindings.get(simpleName) ?? [];
if (existing.some((b) => b.def.nodeId === memberDef.nodeId)) continue;
existing.push({ def: memberDef, origin: 'import' });
scopeBindings.set(simpleName, existing);
// Append to the augmentation bucket for the importer's module
// scope. `findCallableBindingInScope` reads via
// `lookupBindingsAt`, which fans out across `bindings` +
// `bindingAugmentations`.
const bucketArr = getAugmentationBucket(augmentations, moduleScope.id, simpleName);
if (bucketArr.some((b) => b.def.nodeId === memberDef.nodeId)) continue;
bucketArr.push({ def: memberDef, origin: 'import' });
}
}
}
@ -337,15 +342,9 @@ export function populateCsharpNamespaceSiblings(
const q = def.qualifiedName ?? '';
const simpleName = q.includes('.') ? q.slice(q.lastIndexOf('.') + 1) : q;
if (simpleName === '') continue;
let scopeBindings = finalized.get(moduleScope.id);
if (scopeBindings === undefined) {
scopeBindings = new Map<string, BindingRef[]>();
finalized.set(moduleScope.id, scopeBindings);
}
const existing = scopeBindings.get(simpleName) ?? [];
if (existing.some((b) => b.def.nodeId === def.nodeId)) continue;
existing.push({ def, origin: 'namespace' });
scopeBindings.set(simpleName, existing);
const bucketArr = getAugmentationBucket(augmentations, moduleScope.id, simpleName);
if (bucketArr.some((b) => b.def.nodeId === def.nodeId)) continue;
bucketArr.push({ def, origin: 'namespace' });
}
}
}
@ -366,11 +365,6 @@ export function populateCsharpNamespaceSiblings(
}
for (const { scopeId, filePath } of bucket.scopes) {
let scopeBindings = finalized.get(scopeId);
if (scopeBindings === undefined) {
scopeBindings = new Map<string, BindingRef[]>();
finalized.set(scopeId, scopeBindings);
}
for (const [name, defs] of defsByName) {
// Skip names already present locally — `origin: 'local'` in
// scope.bindings would naturally shadow the cross-file
@ -378,18 +372,42 @@ export function populateCsharpNamespaceSiblings(
const local = bucket.scopes.find((s) => s.filePath === filePath)?.scope.bindings.get(name);
if (local !== undefined && local.some((b) => b.origin === 'local')) continue;
const existing = scopeBindings.get(name) ?? [];
let bucketArr: BindingRef[] | null = null;
for (const def of defs) {
if (def.filePath === filePath) continue; // don't self-reference
if (existing.some((b) => b.def.nodeId === def.nodeId)) continue;
existing.push({ def, origin: 'namespace' });
if (bucketArr === null) bucketArr = getAugmentationBucket(augmentations, scopeId, name);
if (bucketArr.some((b) => b.def.nodeId === def.nodeId)) continue;
bucketArr.push({ def, origin: 'namespace' });
}
if (existing.length > 0) scopeBindings.set(name, existing);
}
}
}
}
/** Get-or-create a mutable inner bucket inside the `bindingAugmentations`
* channel. The inner arrays here are mutable by contract (see
* `ScopeResolutionIndexes.bindingAugmentations` doc + scope-resolver I8);
* callers may `push` directly. Allocating the outer/inner Maps lazily
* keeps the augmentation footprint zero for files with no cross-file
* fanout. */
function getAugmentationBucket(
augmentations: Map<ScopeId, Map<string, BindingRef[]>>,
scopeId: ScopeId,
name: string,
): BindingRef[] {
let scopeBindings = augmentations.get(scopeId);
if (scopeBindings === undefined) {
scopeBindings = new Map<string, BindingRef[]>();
augmentations.set(scopeId, scopeBindings);
}
let bucketArr = scopeBindings.get(name);
if (bucketArr === undefined) {
bucketArr = [];
scopeBindings.set(name, bucketArr);
}
return bucketArr;
}
function isTypeDef(def: SymbolDefinition): boolean {
return (
def.type === 'Class' ||

View file

@ -23,6 +23,7 @@ import { getPythonParser, getPythonScopeQuery } from './query.js';
import { synthesizeReceiverTypeBinding } from './receiver-binding.js';
import { computePythonArityMetadata } from './arity-metadata.js';
import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
import { getTreeSitterBufferSize } from '../../constants.js';
export function emitPythonScopeCaptures(
sourceText: string,
@ -36,7 +37,9 @@ export function emitPythonScopeCaptures(
// here at the use site.
let tree = cachedTree as ReturnType<ReturnType<typeof getPythonParser>['parse']> | undefined;
if (tree === undefined) {
tree = getPythonParser().parse(sourceText);
tree = getPythonParser().parse(sourceText, undefined, {
bufferSize: getTreeSitterBufferSize(sourceText),
});
recordCacheMiss();
} else {
recordCacheHit();

View file

@ -37,6 +37,7 @@ import { getTsParser, getTsScopeQuery, tsCachedTreeMatchesGrammar } from './quer
import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
import { synthesizeTsReceiverBinding } from './receiver-binding.js';
import { computeTsArityMetadata } from './arity-metadata.js';
import { getTreeSitterBufferSize } from '../../constants.js';
/** tree-sitter-typescript node types for function-like scopes that may
* carry a synthesized `this` binding. Kept in sync with the
@ -125,7 +126,9 @@ export function emitTsScopeCaptures(
tree = undefined;
}
if (tree === undefined) {
tree = getTsParser(filePath).parse(sourceText);
tree = getTsParser(filePath).parse(sourceText, undefined, {
bufferSize: getTreeSitterBufferSize(sourceText),
});
recordCacheMiss();
} else {
recordCacheHit();

View file

@ -62,8 +62,21 @@ export interface ScopeResolutionIndexes {
readonly methodDispatch: MethodDispatchIndex;
/** Finalized `ImportEdge[]` per module scope. */
readonly imports: ReadonlyMap<ScopeId, readonly ImportEdge[]>;
/** Merged bindings (local + imports + wildcards) per module scope. */
/** Finalize-output bindings (local + imports + wildcards) per module scope.
* Inner `BindingRef[]` arrays are frozen by `materializeBindings`;
* this channel is permanently immutable post-finalize. Consumers
* MUST read via `lookupBindingsAt` so the augmentation channel is
* consulted alongside. See I8 in `contract/scope-resolver.ts`. */
readonly bindings: ReadonlyMap<ScopeId, ReadonlyMap<string, readonly BindingRef[]>>;
/** Append-only post-finalize augmentation channel. Populated by
* language hooks such as `populateNamespaceSiblings` for cross-file
* bindings synthesized after finalize (e.g. C# same-namespace
* visibility, `using static` member exposure). Inner arrays are
* NOT frozen hooks `push()` directly. Walkers must consult both
* this map and `bindings` via `lookupBindingsAt`; finalized refs
* are returned first and win duplicate `def.nodeId` metadata, with
* unique augmentations appended after. See I8. */
readonly bindingAugmentations: ReadonlyMap<ScopeId, ReadonlyMap<string, readonly BindingRef[]>>;
/** Pre-resolution usage facts; consumed by the resolution phase. */
readonly referenceSites: readonly ReferenceSite[];
/** SCC condensation of the file-level import graph callers that want

View file

@ -48,7 +48,11 @@ import type {
FileScopeBindings,
ExtractedORMQuery,
} from './workers/parse-worker.js';
import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from './constants.js';
import {
getTreeSitterBufferSize,
getTreeSitterContentByteLength,
TREE_SITTER_MAX_BUFFER,
} from './constants.js';
export type FileProgressCallback = (current: number, total: number, filePath: string) => void;
@ -352,7 +356,7 @@ const processParsingSequential = async (
}
// Skip files larger than the max tree-sitter buffer (32 MB)
if (file.content.length > TREE_SITTER_MAX_BUFFER) continue;
if (getTreeSitterContentByteLength(file.content) > TREE_SITTER_MAX_BUFFER) continue;
// Vue SFC preprocessing: extract <script> block content
let parseContent = file.content;
@ -375,7 +379,7 @@ const processParsingSequential = async (
let tree: Parser.Tree;
try {
tree = parser.parse(parseContent, undefined, {
bufferSize: getTreeSitterBufferSize(parseContent.length),
bufferSize: getTreeSitterBufferSize(parseContent),
});
} catch (parseError) {
console.warn(`Skipping unparseable file: ${file.path}`);

View file

@ -136,14 +136,45 @@
* once per workspace at resolve time), and merging would create a
* god-interface that complicates future migrations.
*
* - **I8 Post-finalize hooks may mutate `Scope.typeBindings` and
* `indexes.bindings`.** `propagateImportedReturnTypes` and
* `populateNamespaceSiblings` both write to these structures via
* `as Map<...>` casts through `ReadonlyMap` facades. Downstream
* consumers MUST NOT freeze or snapshot these maps before all
* post-finalize hooks have run. The `ReadonlyMap<...>` type on
* `ScopeResolutionIndexes` is a read-guidance surface for
* consumers, NOT an immutability promise during the resolve phase.
* - **I8 Two-channel binding lifecycle.**
* `indexes.bindings` is the **finalize-output channel**. After
* `finalizeScopeModel` returns, its inner `BindingRef[]` arrays
* are deep-frozen by `materializeBindings` and MUST NOT be
* mutated by any post-finalize hook. Treat `indexes.bindings` as
* immutable from the moment `finalizeScopeModel` returns.
*
* `indexes.bindingAugmentations` is the **post-finalize
* append-only channel**. Hooks like `populateNamespaceSiblings`
* append cross-file bindings synthesized after finalize (C#
* same-namespace visibility, `using static` member exposure)
* into this channel, NOT into `indexes.bindings`. Inner arrays
* here are NEVER frozen hooks `push()` directly. Any consumer
* that reads post-finalize workspace bindings MUST query both
* index channels via `lookupBindingsAt`
* (`scope-resolution/scope/walkers.ts`); the helper returns
* finalized refs first, appends unique augmentation refs after,
* and dedupes by `def.nodeId` so finalized metadata wins on
* duplicate defs. Per-`Scope.bindings` local declarations are the
* lexical extraction channel and remain a separate first-tier
* lookup for local shadowing.
*
* `Scope.typeBindings` remains mutable post-finalize per I6 (it
* is intentionally not frozen at any point).
*
* The `ReadonlyMap<...>` types on `ScopeResolutionIndexes` are
* compile-time read-guidance for consumers; structural mutation
* of `bindingAugmentations` is performed via a deliberate
* `as Map<...>` cast inside the hook implementations and is the
* ONLY sanctioned channel for post-finalize binding fanout.
*
* The dev-mode runtime validator
* (`validateBindingsImmutability` in
* `scope-resolution/validate-bindings-immutability.ts`) surfaces
* any drift i.e. a hook writing to `indexes.bindings` instead
* of `bindingAugmentations`, or producing a non-frozen finalized
* bucket via `onWarn` when explicitly enabled by
* `NODE_ENV === 'development' || VALIDATE_SEMANTIC_MODEL === '1'`
* (`VALIDATE_SEMANTIC_MODEL=0` is an explicit off switch).
*
* - **I9 `SemanticModel` is the single authoritative symbol store.**
* Every symbol-indexed lookup (key = `nodeId | simpleName |

View file

@ -42,6 +42,7 @@
import type { ParsedFile, ScopeId, TypeRef } from 'gitnexus-shared';
import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
import type { WorkspaceResolutionIndex } from '../workspace-index.js';
import { lookupBindingsAt, namesAtScope } from '../scope/walkers.js';
/**
* Max chain depth for the post-finalize re-follow. Effective end-to-end
@ -136,14 +137,19 @@ export function propagateImportedReturnTypes(
for (const filePath of scc.files) {
const importerModule = moduleScopeByFile.get(filePath);
if (importerModule === undefined) continue;
const finalizedBindings = indexes.bindings.get(importerModule.id);
if (finalizedBindings === undefined) continue;
for (const [localName, refs] of finalizedBindings) {
// Iterate finalized + augmented binding names at this scope so
// post-finalize hooks (e.g. `using static` augmentations from
// `populateCsharpNamespaceSiblings`) are visible to the
// import-derived typeBinding mirror. Both helpers fast-path when
// no augmentations exist for the scope, so the common case is
// allocation-free. See I8.
for (const localName of namesAtScope(importerModule.id, indexes)) {
// Skip if importer already has a typeBinding for this name —
// an explicit local annotation must win over import-derived.
if (importerModule.typeBindings.has(localName)) continue;
const refs = lookupBindingsAt(importerModule.id, localName, indexes);
for (const ref of refs) {
if (ref.origin !== 'import' && ref.origin !== 'reexport') continue;
const sourceModule = moduleScopeByFile.get(ref.def.filePath);

View file

@ -36,7 +36,7 @@ import { SupportedLanguages, getLanguageFromFilename } from 'gitnexus-shared';
import { readFileContents } from '../../filesystem-walker.js';
import { runScopeResolution } from './run.js';
import { SCOPE_RESOLVERS } from './registry.js';
import { isDev } from '../../utils/env.js';
import { isDev, isSemanticModelValidatorEnabled } from '../../utils/env.js';
export interface ScopeResolutionOutput {
/** True when at least one language ran. */
@ -143,7 +143,9 @@ export const scopeResolutionPhase: PipelinePhase<ScopeResolutionOutput> = {
treeCache: scopeTreeCache,
resolutionConfig,
onWarn: (msg) => {
if (isDev) console.warn(`[scope-resolution:${lang}] ${msg}`);
if (isSemanticModelValidatorEnabled()) {
console.warn(`[scope-resolution:${lang}] ${msg}`);
}
},
},
provider,

View file

@ -27,6 +27,7 @@ import type { ParsedFile, RegistryProviders } from 'gitnexus-shared';
import type { KnowledgeGraph } from '../../../graph/types.js';
import type { MutableSemanticModel, SemanticModel } from '../../model/semantic-model.js';
import { reconcileOwnership, validateOwnershipParity } from './reconcile-ownership.js';
import { validateBindingsImmutability } from './validate-bindings-immutability.js';
import { extractParsedFile } from '../../scope-extractor-bridge.js';
import { finalizeScopeModel } from '../../finalize-orchestrator.js';
import { resolveReferenceSites, type ResolveStats } from '../../resolve-references.js';
@ -174,6 +175,8 @@ export function runScopeResolution(
// Cross-file implicit-namespace visibility (C#). Must run before
// propagateImportedReturnTypes so the latter pass sees siblings'
// class bindings when chasing return-type chains across files.
// The hook writes to `bindingAugmentations` only; finalized
// `indexes.bindings` remains immutable post-finalize (I8).
if (provider.populateNamespaceSiblings !== undefined) {
const fileContents = new Map<string, string>();
for (const f of files) fileContents.set(f.path, f.content);
@ -195,6 +198,14 @@ export function runScopeResolution(
}
const tPropagate = PROF ? process.hrtime.bigint() : 0n;
// Opt-in I8 invariant guard. Runs once after all post-finalize hooks
// (`populateNamespaceSiblings`, `propagateImportedReturnTypes`) have
// had a chance to drift, so a single sweep covers the full
// post-finalize surface visible to `resolveReferenceSites`. No-op in
// default CLI runs; enabled by NODE_ENV=development or
// VALIDATE_SEMANTIC_MODEL=1.
validateBindingsImmutability(indexes, onWarn);
// ── Phase 3: resolve references via Registry.lookup ────────────────────
const registryProviders: RegistryProviders = {
arityCompatibility: provider.arityCompatibility,

View file

@ -0,0 +1,78 @@
/**
* Dev-mode runtime validator for the two-channel binding lifecycle
* (Contract Invariant I8 in `contract/scope-resolver.ts`).
*
* The two channels:
* - `indexes.bindings` finalize-output channel. After
* `finalizeScopeModel` returns, every inner `BindingRef[]` array
* here is deep-frozen by `materializeBindings`. NO post-finalize
* hook should ever mutate this map's inner arrays drift here
* manifests at runtime as the `Cannot add property N, object is
* not extensible` crash (issue #1066) or, more insidiously, as
* a hook silently mutating one of the frozen arrays (a no-op in
* production where freezes can be elided, a `TypeError` in dev).
*
* - `indexes.bindingAugmentations` post-finalize append-only
* channel. Inner arrays here are NEVER frozen; hooks like
* `populateNamespaceSiblings` `push()` directly. Walkers consult
* both channels via `lookupBindingsAt`.
*
* This validator runs after every post-finalize hook has executed
* (so the dev-mode envelope captures the FULL surface area visible
* to `resolveReferenceSites`) and asserts:
*
* 1. Every inner `BindingRef[]` array in `indexes.bindings` is
* `Object.isFrozen` i.e. finalize produced a frozen bucket
* AND no hook accidentally `set()`-back a mutable replacement.
*
* 2. Every inner `BindingRef[]` array in
* `indexes.bindingAugmentations` is NOT frozen i.e. the
* hook used the augmentation channel as designed (mutable
* `push()`) and didn't accidentally freeze its own scratch
* arrays. Self-documenting; mostly a sanity net.
*
* Mirrors `validateOwnershipParity` (#909): warns via `onWarn`,
* never throws, and is opt-in outside development. Gated by
* `isSemanticModelValidatorEnabled()` (`utils/env.ts`).
*/
import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
import { isSemanticModelValidatorEnabled } from '../../utils/env.js';
export function validateBindingsImmutability(
indexes: ScopeResolutionIndexes,
onWarn: (message: string) => void,
): number {
if (!isSemanticModelValidatorEnabled()) return 0;
let violations = 0;
for (const [scopeId, bucketMap] of indexes.bindings) {
for (const [name, bucket] of bucketMap) {
if (!Object.isFrozen(bucket)) {
onWarn(
`binding-immutability: indexes.bindings[${scopeId}][${name}] is NOT frozen — ` +
`finalize produced a mutable bucket OR a post-finalize hook replaced a frozen ` +
`bucket with a mutable one. Hooks must write to indexes.bindingAugmentations ` +
`instead. See ScopeResolver Invariant I8.`,
);
violations++;
}
}
}
for (const [scopeId, bucketMap] of indexes.bindingAugmentations) {
for (const [name, bucket] of bucketMap) {
if (Object.isFrozen(bucket)) {
onWarn(
`binding-immutability: indexes.bindingAugmentations[${scopeId}][${name}] is FROZEN — ` +
`the augmentation channel is mutable by contract; freezing it defeats the ` +
`append-only purpose. See ScopeResolver Invariant I8.`,
);
violations++;
}
}
}
return violations;
}

View file

@ -1,13 +1,14 @@
/**
* Scope-chain lookup primitives shared across language providers.
*
* Four functions:
* Five functions:
* - `findReceiverTypeBinding` walk scope.typeBindings up the chain
* for a receiver name.
* - `findClassBindingInScope` walk scope.bindings + indexes.bindings
* (pre-finalize + post-finalize) for a class-kind binding. Dual-
* source is required because the cross-file finalize pass produces
* a separate bindings map that is not merged back into scope.bindings.
* - `lookupBindingsAt` read finalized + augmented binding refs at
* one scope, deduped by `def.nodeId`. The dual-source-aware
* primitive every other binding lookup composes with.
* - `findClassBindingInScope` walk scope.bindings + the indexes via
* `lookupBindingsAt` for a class-kind binding.
* - `findOwnedMember` find a method/field owned by a class def
* across all parsed files by (ownerId, simpleName).
* - `findExportedDef` find a file-level exported def (top-of-module
@ -19,11 +20,85 @@
* as-is for TypeScript, Java, Kotlin, Ruby, etc.
*/
import type { ParsedFile, ScopeId, SymbolDefinition, TypeRef } from 'gitnexus-shared';
import type { BindingRef, ParsedFile, ScopeId, SymbolDefinition, TypeRef } from 'gitnexus-shared';
import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
import type { SemanticModel } from '../../model/semantic-model.js';
import type { WorkspaceResolutionIndex } from '../workspace-index.js';
const EMPTY_BINDINGS: readonly BindingRef[] = Object.freeze([]);
/**
* Look up binding refs at `scopeId` for `name`, consulting both the
* finalize-owned `bindings` channel and the post-finalize
* `bindingAugmentations` channel (see invariant I8 in
* `contract/scope-resolver.ts`). Finalized refs come first; augmented
* refs append, deduped by `def.nodeId` so a sibling that's also
* explicitly imported doesn't double-emit.
*
* Returns a shared frozen empty array when neither channel has the
* name callers can compare against `=== EMPTY_BINDINGS` if they
* want a fast-path miss check. The bucket arrays are returned by
* reference when only one channel populates them; the merged path
* allocates a fresh array.
*
* Walker primitives (`findClassBindingInScope`,
* `findCallableBindingInScope`, `findExportedDefByName`) and
* post-finalize passes that read finalized bindings (e.g.
* `propagateImportedReturnTypes`, `namespace-targets`) MUST go
* through this helper instead of `scopes.bindings.get(...)` directly,
* so the augmentation channel is always visible.
*/
export function lookupBindingsAt(
scopeId: ScopeId,
name: string,
scopes: ScopeResolutionIndexes,
): readonly BindingRef[] {
const finalized = scopes.bindings.get(scopeId)?.get(name);
const augmented = scopes.bindingAugmentations.get(scopeId)?.get(name);
const fLen = finalized?.length ?? 0;
const aLen = augmented?.length ?? 0;
if (fLen === 0 && aLen === 0) return EMPTY_BINDINGS;
if (aLen === 0) return finalized!;
if (fLen === 0) return augmented!;
const seen = new Set<string>();
const out: BindingRef[] = [];
for (const r of finalized!) {
seen.add(r.def.nodeId);
out.push(r);
}
for (const r of augmented!) {
if (seen.has(r.def.nodeId)) continue;
out.push(r);
}
return out;
}
const EMPTY_NAMES: Iterable<string> = Object.freeze([]) as readonly string[];
/**
* Return the union of bound names at `scopeId` across both the
* finalized and augmented channels. Companion to `lookupBindingsAt`
* for callers that need to iterate every name at a scope (e.g.
* `propagateImportedReturnTypes`). Order is not guaranteed; callers
* that need stable iteration should sort externally.
*
* Fast paths (zero allocation) when at most one channel is populated:
* returns the underlying `Map.keys()` iterator directly. Only when both
* channels carry names do we materialize a `Set` for deduplication.
*/
export function namesAtScope(scopeId: ScopeId, scopes: ScopeResolutionIndexes): Iterable<string> {
const finalized = scopes.bindings.get(scopeId);
const augmented = scopes.bindingAugmentations.get(scopeId);
const fSize = finalized?.size ?? 0;
const aSize = augmented?.size ?? 0;
if (fSize === 0 && aSize === 0) return EMPTY_NAMES;
if (aSize === 0) return finalized!.keys();
if (fSize === 0) return augmented!.keys();
const out = new Set<string>(finalized!.keys());
for (const name of augmented!.keys()) out.add(name);
return out;
}
/**
* True when a def's `type` names a class-like declaration every kind
* that collapses to `@scope.class` in the scope-extractor query contract.
@ -78,8 +153,10 @@ export function findReceiverTypeBinding(
* Walks the scope chain upward and consults TWO sources at each step:
* 1. `scope.bindings` populated during scope-extraction Pass 2 with
* local declarations (`origin: 'local'`).
* 2. `indexes.bindings` populated by the cross-file finalize pass
* with import/namespace/wildcard/reexport origins.
* 2. The cross-file finalized + augmented bindings, via
* `lookupBindingsAt` (per I8: finalized = canonical immutable
* output; augmented = post-finalize hooks like
* `populateNamespaceSiblings`).
*
* Without (2) we'd miss every cross-file class-receiver call.
*/
@ -103,12 +180,9 @@ export function findClassBindingInScope(
}
}
const finalizedScopeBindings = scopes.bindings.get(currentId);
const importedBindings = finalizedScopeBindings?.get(receiverName);
if (importedBindings !== undefined) {
for (const b of importedBindings) {
if (isClassLike(b.def.type)) return b.def;
}
const importedBindings = lookupBindingsAt(currentId, receiverName, scopes);
for (const b of importedBindings) {
if (isClassLike(b.def.type)) return b.def;
}
currentId = scope.parent;
@ -119,8 +193,9 @@ export function findClassBindingInScope(
/**
* Look up a callable (Function/Method/Constructor) by name in the
* given scope's chain. Uses the dual-source pattern (scope.bindings +
* indexes.bindings) so cross-file imports are visible without it
* free calls to imported functions never resolve via the post-pass.
* `lookupBindingsAt` for finalized + augmented) so cross-file
* imports are visible without it free calls to imported functions
* never resolve via the post-pass.
*
* Mirrors `findClassBindingInScope` exactly; only the accepted
* def-type predicate differs.
@ -147,13 +222,10 @@ export function findCallableBindingInScope(
}
}
const finalizedScopeBindings = scopes.bindings.get(currentId);
const importedBindings = finalizedScopeBindings?.get(callableName);
if (importedBindings !== undefined) {
for (const b of importedBindings) {
if (b.def.type === 'Function' || b.def.type === 'Method' || b.def.type === 'Constructor') {
return b.def;
}
const importedBindings = lookupBindingsAt(currentId, callableName, scopes);
for (const b of importedBindings) {
if (b.def.type === 'Function' || b.def.type === 'Method' || b.def.type === 'Constructor') {
return b.def;
}
}
@ -294,11 +366,9 @@ export function findExportedDefByName(
if (b.def.type === 'Function' || b.def.type === 'Method') return b.def;
}
}
const finalized = scopes.bindings.get(currentId)?.get(name);
if (finalized !== undefined) {
for (const b of finalized) {
if (b.def.type === 'Function' || b.def.type === 'Method') return b.def;
}
const finalized = lookupBindingsAt(currentId, name, scopes);
for (const b of finalized) {
if (b.def.type === 'Function' || b.def.type === 'Method') return b.def;
}
currentId = scope.parent;
}
@ -355,7 +425,12 @@ export function findOwnedMember(
* excluded.
*
* Reads from `WorkspaceResolutionIndex.moduleScopeByFile` (scope-tied
* lookup that doesn't live on `SemanticModel`).
* lookup that doesn't live on `SemanticModel`). This intentionally
* does NOT call `lookupBindingsAt`: `findExportedDef` answers "what
* did the target file declare locally at module scope?", while
* `bindingAugmentations` models importer-side visibility created by
* post-finalize hooks. Callers that need importer-visible exports use
* `findExportedDefByName`, which is dual-channel aware.
*/
export function findExportedDef(
targetFile: string,

View file

@ -9,3 +9,17 @@
/** Whether we're running in development mode (enables verbose console logging). */
export const isDev = process.env.NODE_ENV === 'development';
/**
* Whether scope-resolution dev validators (e.g. `validateBindingsImmutability`)
* should run AND emit warnings. Off by default in CLI runs to avoid silent
* O(n) scans on large repos; on in `NODE_ENV=development` or when explicitly
* opted-in via `VALIDATE_SEMANTIC_MODEL=1`. `VALIDATE_SEMANTIC_MODEL=0` is the
* explicit off switch and wins over both.
*
* Read every call (not memoized) so test setups using `vi.stubEnv` work.
*/
export const isSemanticModelValidatorEnabled = (): boolean => {
if (process.env.VALIDATE_SEMANTIC_MODEL === '0') return false;
return process.env.NODE_ENV === 'development' || process.env.VALIDATE_SEMANTIC_MODEL === '1';
};

View file

@ -15,7 +15,11 @@ import Ruby from 'tree-sitter-ruby';
import { createRequire } from 'node:module';
import { SupportedLanguages } from 'gitnexus-shared';
import { getProvider } from '../languages/index.js';
import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from '../constants.js';
import {
getTreeSitterBufferSize,
getTreeSitterContentByteLength,
TREE_SITTER_MAX_BUFFER,
} from '../constants.js';
import type { SymbolTableReader } from '../model/symbol-table.js';
import type { ExtractedHeritage } from '../model/heritage-map.js';
@ -1383,7 +1387,7 @@ const processFileGroup = (
for (const file of files) {
// Skip files larger than the max tree-sitter buffer (32 MB)
if (file.content.length > TREE_SITTER_MAX_BUFFER) continue;
if (getTreeSitterContentByteLength(file.content) > TREE_SITTER_MAX_BUFFER) continue;
// Vue SFC preprocessing: extract <script> block content
let parseContent = file.content;
@ -1402,7 +1406,7 @@ const processFileGroup = (
let tree;
try {
tree = parser.parse(parseContent, undefined, {
bufferSize: getTreeSitterBufferSize(parseContent.length),
bufferSize: getTreeSitterBufferSize(parseContent),
});
} catch (err) {
console.warn(

View file

@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,6 @@
namespace App.Models;
public class User
{
public void Save() { }
}

View file

@ -0,0 +1,3 @@
namespace App.Other;
public class Helper { }

View file

@ -0,0 +1,548 @@
using App.Models;
using App.Other;
namespace App.Services;
// ============================================================================
// Regression fixture for GitNexus issue #1066 (PR #1050 follow-up).
//
// This file is intentionally larger than the 32 KB tree-sitter default buffer.
// The scope-resolution phase reparses the file on cache miss (worker mode
// can't share `Tree` instances across MessageChannels), so the cache-miss
// path must use the adaptive `getTreeSitterBufferSize` rather than the
// default. Without that, this file fails with `Invalid argument` and the
// `CreateUser -> Save` CALLS edge below would silently disappear.
//
// In addition, this file declares a local `Helper` while also importing
// `App.Other.Helper` via `using App.Other;`. Before the freeze fix in
// `populateCsharpNamespaceSiblings`, the cross-namespace inject loop pushed
// `App.Other.Helper` onto the frozen `bindings[module]['Helper']` bucket
// produced by `finalize-algorithm.ts` (`Object.freeze(refs.slice())`),
// throwing `Cannot add property N, object is not extensible` and aborting
// the whole `scopeResolution` phase.
//
// Both regressions are exercised end-to-end through the full pipeline by
// the resolver test in `test/integration/resolvers/csharp.test.ts`.
// ============================================================================
//
// Padding section below — each line is a separate `comment` AST node so the
// resolver pipeline treats them as a no-op while the source byte count
// crosses the 32 KB threshold.
// Pad 001 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 002 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 003 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 004 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 005 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 006 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 007 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 008 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 009 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 010 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 011 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 012 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 013 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 014 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 015 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 016 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 017 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 018 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 019 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 020 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 021 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 022 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 023 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 024 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 025 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 026 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 027 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 028 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 029 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 030 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 031 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 032 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 033 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 034 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 035 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 036 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 037 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 038 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 039 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 040 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 041 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 042 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 043 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 044 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 045 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 046 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 047 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 048 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 049 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 050 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 051 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 052 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 053 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 054 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 055 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 056 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 057 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 058 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 059 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 060 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 061 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 062 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 063 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 064 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 065 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 066 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 067 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 068 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 069 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 070 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 071 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 072 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 073 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 074 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 075 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 076 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 077 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 078 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 079 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 080 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 081 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 082 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 083 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 084 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 085 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 086 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 087 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 088 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 089 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 090 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 091 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 092 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 093 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 094 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 095 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 096 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 097 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 098 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 099 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 100 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 101 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 102 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 103 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 104 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 105 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 106 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 107 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 108 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 109 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 110 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 111 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 112 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 113 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 114 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 115 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 116 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 117 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 118 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 119 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 120 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 121 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 122 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 123 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 124 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 125 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 126 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 127 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 128 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 129 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 130 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 131 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 132 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 133 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 134 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 135 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 136 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 137 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 138 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 139 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 140 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 141 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 142 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 143 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 144 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 145 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 146 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 147 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 148 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 149 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 150 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 151 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 152 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 153 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 154 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 155 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 156 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 157 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 158 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 159 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 160 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 161 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 162 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 163 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 164 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 165 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 166 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 167 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 168 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 169 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 170 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 171 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 172 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 173 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 174 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 175 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 176 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 177 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 178 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 179 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 180 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 181 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 182 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 183 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 184 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 185 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 186 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 187 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 188 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 189 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 190 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 191 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 192 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 193 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 194 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 195 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 196 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 197 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 198 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 199 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 200 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 201 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 202 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 203 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 204 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 205 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 206 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 207 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 208 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 209 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 210 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 211 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 212 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 213 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 214 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 215 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 216 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 217 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 218 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 219 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 220 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 221 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 222 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 223 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 224 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 225 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 226 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 227 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 228 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 229 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 230 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 231 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 232 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 233 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 234 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 235 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 236 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 237 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 238 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 239 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 240 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 241 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 242 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 243 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 244 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 245 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 246 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 247 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 248 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 249 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 250 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 251 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 252 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 253 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 254 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 255 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 256 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 257 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 258 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 259 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 260 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 261 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 262 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 263 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 264 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 265 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 266 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 267 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 268 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 269 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 270 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 271 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 272 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 273 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 274 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 275 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 276 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 277 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 278 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 279 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 280 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 281 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 282 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 283 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 284 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 285 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 286 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 287 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 288 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 289 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 290 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 291 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 292 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 293 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 294 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 295 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 296 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 297 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 298 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 299 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 300 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 301 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 302 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 303 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 304 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 305 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 306 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 307 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 308 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 309 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 310 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 311 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 312 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 313 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 314 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 315 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 316 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 317 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 318 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 319 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 320 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 321 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 322 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 323 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 324 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 325 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 326 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 327 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 328 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 329 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 330 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 331 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 332 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 333 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 334 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 335 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 336 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 337 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 338 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 339 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 340 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 341 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 342 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 343 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 344 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 345 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 346 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 347 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 348 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 349 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 350 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 351 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 352 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 353 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 354 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 355 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 356 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 357 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 358 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 359 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 360 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 361 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 362 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 363 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 364 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 365 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 366 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 367 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 368 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 369 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 370 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 371 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 372 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 373 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 374 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 375 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 376 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 377 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 378 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 379 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 380 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 381 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 382 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 383 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 384 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 385 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 386 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 387 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 388 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 389 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 390 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 391 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 392 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 393 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 394 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 395 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 396 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 397 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 398 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 399 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 400 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 401 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 402 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 403 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 404 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 405 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 406 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 407 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 408 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 409 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 410 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 411 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 412 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 413 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 414 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 415 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 416 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 417 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 418 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 419 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 420 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 421 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 422 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 423 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 424 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 425 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 426 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 427 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 428 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 429 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 430 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 431 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 432 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 433 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 434 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 435 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 436 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 437 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 438 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 439 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 440 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 441 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 442 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 443 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 444 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 445 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 446 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 447 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 448 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 449 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 450 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 451 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 452 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 453 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 454 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 455 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 456 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 457 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 458 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 459 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 460 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 461 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 462 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 463 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 464 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 465 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 466 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 467 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 468 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 469 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 470 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 471 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 472 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 473 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 474 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 475 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 476 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 477 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 478 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 479 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 480 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 481 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 482 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 483 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 484 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 485 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 486 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 487 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 488 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 489 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 490 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 491 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 492 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 493 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 494 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 495 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 496 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 497 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 498 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 499 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// Pad 500 - large-file marker for issue #1066 cache-miss buffer regression. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.
// ============================================================================
// Cross-namespace `using` plus a colliding local `Helper` together exercise
// the frozen-bucket fix in populateCsharpNamespaceSiblings. The CreateUser
// method below resolves `User` via same-namespace siblings (Models/User.cs)
// and `user.Save()` via receiver-typed dispatch.
// ============================================================================
public class Helper { }
public class UserService
{
public void CreateUser()
{
var user = new User();
user.Save();
}
}

View file

@ -2420,3 +2420,68 @@ describe('C# class-name receiver write ACCESSES (merged Case 2 kind-aware branch
expect(stray).toEqual([]);
});
});
// ---------------------------------------------------------------------------
// Issue #1066 regression: large source files (>32 KB) combined with a
// cross-namespace `using` and a colliding local class. Pins both fixes in
// the resolver dataset:
//
// 1. emitCsharpScopeCaptures + extractFileStructure must use the adaptive
// `getTreeSitterBufferSize` on cache miss, otherwise UserService.cs
// fails to reparse with "Invalid argument" and CreateUser is dropped.
// 2. populateCsharpNamespaceSiblings must append to bindingAugmentations
// instead of mutating frozen finalize-produced BindingRef[] arrays;
// otherwise the cross-namespace inject loop throws "Cannot add property
// N, object is not extensible" when the importer also declares the same
// simple name locally.
// ---------------------------------------------------------------------------
describe('C# large-file + frozen-bucket regression (issue #1066)', () => {
let result: PipelineResult;
beforeAll(async () => {
// Force the worker pool path with low thresholds so the scope-resolution
// cache-miss reparse actually fires (workers can't share Tree instances
// across MessageChannels). This is what reproduces the >32 KB
// "Invalid argument" failure end-to-end through the pipeline.
result = await runPipelineFromRepo(
path.join(FIXTURES, 'csharp-large-cache-miss-resolution'),
() => {},
{ workerThresholdsForTest: { minFiles: 1, minBytes: 0 } },
);
}, 120000);
it('extracts UserService.CreateUser despite the >32 KB source size', () => {
// Without the adaptive-buffer fix, UserService.cs would fail to reparse
// on cache miss and CreateUser would not be extracted at all.
expect(getNodesByLabel(result, 'Method')).toEqual(
expect.arrayContaining(['CreateUser', 'Save']),
);
});
it('detects all three classes (User, Helper, UserService)', () => {
expect(getNodesByLabel(result, 'Class')).toEqual(
expect.arrayContaining(['User', 'Helper', 'UserService']),
);
});
it('resolves CreateUser -> User constructor across same namespace', () => {
const calls = getRelationships(result, 'CALLS');
const ctor = calls.find(
(c) => c.source === 'CreateUser' && c.target === 'User' && c.targetLabel === 'Class',
);
expect(ctor).toBeDefined();
expect(ctor!.targetFilePath).toBe('Models/User.cs');
});
it('resolves CreateUser -> Save through namespace siblings', () => {
// Without the freeze fix, populateCsharpNamespaceSiblings throws on
// the colliding `Helper` bucket, aborting the whole scopeResolution
// phase, so no CALLS edges from CreateUser would be emitted at all.
const calls = getRelationships(result, 'CALLS');
const save = calls.find((c) => c.source === 'CreateUser' && c.target === 'Save');
expect(save).toBeDefined();
expect(save!.targetFilePath).toBe('Models/User.cs');
expect(['import-resolved', 'global']).toContain(save!.rel.reason);
});
});

View file

@ -1,8 +1,10 @@
/**
* Python: relative imports + class inheritance + ambiguous module disambiguation
*/
import { describe, it, expect, beforeAll } from 'vitest';
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import path from 'path';
import fs from 'node:fs';
import os from 'node:os';
import {
FIXTURES,
CROSS_FILE_FIXTURES,
@ -14,6 +16,14 @@ import {
type PipelineResult,
} from './helpers.js';
function writeFixtureRepo(root: string, files: Record<string, string>): void {
for (const [relPath, content] of Object.entries(files)) {
const fullPath = path.join(root, relPath);
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
fs.writeFileSync(fullPath, content, 'utf8');
}
}
// ---------------------------------------------------------------------------
// Heritage: relative imports + class inheritance
// ---------------------------------------------------------------------------
@ -2485,3 +2495,61 @@ describe('Python class-body namespace import feeds method receiver-bound call',
expect(callEdge!.rel.targetId).toContain('mod.py:helper');
});
});
// ---------------------------------------------------------------------------
// Issue #1066 sibling regression for Python: force worker-mode extraction so
// scope-resolution reparses on cache miss, then assert large ASCII and
// UTF-8-heavy source files still produce trailing call edges.
// ---------------------------------------------------------------------------
describe('Python large-file cache-miss parser buffer regression', () => {
let repoDir: string;
let result: PipelineResult;
beforeAll(async () => {
repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gn-python-large-cache-'));
writeFixtureRepo(repoDir, {
'models.py': `
class User:
def save(self):
return True
`,
'ascii_app.py': `from models import User
# ${'x'.repeat(120 * 1024)}
def create_ascii_user():
user = User()
user.save()
`,
'utf8_app.py': `from models import User
# ${'漢'.repeat(120_000)}
def create_utf8_user():
user = User()
user.save()
`,
});
result = await runPipelineFromRepo(repoDir, () => {}, {
workerThresholdsForTest: { minFiles: 1, minBytes: 0 },
});
}, 120000);
afterAll(() => {
if (repoDir !== undefined) fs.rmSync(repoDir, { recursive: true, force: true });
});
it('extracts trailing functions after large ASCII and UTF-8 padding', () => {
expect(getNodesByLabel(result, 'Function')).toEqual(
expect.arrayContaining(['create_ascii_user', 'create_utf8_user', 'save']),
);
});
it('resolves calls from both padded files to User.save', () => {
const calls = getRelationships(result, 'CALLS');
for (const source of ['create_ascii_user', 'create_utf8_user']) {
const save = calls.find((c) => c.source === source && c.target === 'save');
expect(save).toBeDefined();
expect(save!.targetFilePath).toBe('models.py');
}
});
});

View file

@ -1,8 +1,10 @@
/**
* TypeScript: heritage resolution + ambiguous symbol disambiguation
*/
import { describe, it, expect, beforeAll } from 'vitest';
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import path from 'path';
import fs from 'node:fs';
import os from 'node:os';
import {
FIXTURES,
getRelationships,
@ -13,6 +15,14 @@ import {
type PipelineResult,
} from './helpers.js';
function writeFixtureRepo(root: string, files: Record<string, string>): void {
for (const [relPath, content] of Object.entries(files)) {
const fullPath = path.join(root, relPath);
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
fs.writeFileSync(fullPath, content, 'utf8');
}
}
// ---------------------------------------------------------------------------
// Heritage: class extends + implements interface
// ---------------------------------------------------------------------------
@ -2815,3 +2825,76 @@ describe('TypeScript namespace re-export barrel (registry-primary)', () => {
expect(fromBarrel).toContain('src/base.ts');
});
});
// ---------------------------------------------------------------------------
// Issue #1066 sibling regression for TypeScript: force worker-mode extraction
// so scope-resolution reparses on cache miss, then assert large ASCII and
// UTF-8-heavy source files still produce trailing call edges.
// ---------------------------------------------------------------------------
describe('TypeScript large-file cache-miss parser buffer regression', () => {
let repoDir: string;
let result: PipelineResult;
beforeAll(async () => {
repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gn-ts-large-cache-'));
writeFixtureRepo(repoDir, {
'src/models.ts': `
export class User {
save(): boolean {
return true;
}
}
`,
'src/ascii-app.ts': `import { User } from './models';
// ${'x'.repeat(120 * 1024)}
export function createAsciiUser(): void {
const user = new User();
user.save();
}
`,
'src/utf8-app.ts': `import { User } from './models';
// ${'漢'.repeat(120_000)}
export function createUtf8User(): void {
const user = new User();
user.save();
}
`,
});
result = await runPipelineFromRepo(repoDir, () => {}, {
workerThresholdsForTest: { minFiles: 1, minBytes: 0 },
});
}, 120000);
afterAll(() => {
if (repoDir !== undefined) fs.rmSync(repoDir, { recursive: true, force: true });
});
it('extracts trailing functions after large ASCII and UTF-8 padding', () => {
expect(getNodesByLabel(result, 'Function')).toEqual(
expect.arrayContaining(['createAsciiUser', 'createUtf8User']),
);
});
it('resolves constructor calls from both padded files to User', () => {
const calls = getRelationships(result, 'CALLS');
for (const source of ['createAsciiUser', 'createUtf8User']) {
const ctor = calls.find(
(c) => c.source === source && c.target === 'User' && c.targetLabel === 'Class',
);
expect(ctor).toBeDefined();
expect(ctor!.targetFilePath).toBe('src/models.ts');
}
});
it('resolves member calls from both padded files to User.save', () => {
const calls = getRelationships(result, 'CALLS');
for (const source of ['createAsciiUser', 'createUtf8User']) {
const save = calls.find((c) => c.source === source && c.target === 'save');
expect(save).toBeDefined();
expect(save!.targetFilePath).toBe('src/models.ts');
}
});
});

View file

@ -6,6 +6,7 @@ import type { NodeLabel } from 'gitnexus-shared';
import type { LanguageProvider } from '../../src/core/ingestion/language-provider.js';
import {
getTreeSitterBufferSize,
getTreeSitterContentByteLength,
TREE_SITTER_BUFFER_SIZE,
TREE_SITTER_MAX_BUFFER,
} from '../../src/core/ingestion/constants.js';
@ -638,56 +639,69 @@ describe('extractFunctionName (via methodExtractor)', () => {
});
describe('getTreeSitterBufferSize', () => {
const expectedBufferSize = (byteLength: number): number =>
Math.min(Math.max(byteLength * 2, TREE_SITTER_BUFFER_SIZE), TREE_SITTER_MAX_BUFFER);
it('returns minimum 512KB for small files', () => {
expect(getTreeSitterBufferSize(100)).toBe(TREE_SITTER_BUFFER_SIZE);
expect(getTreeSitterBufferSize(0)).toBe(TREE_SITTER_BUFFER_SIZE);
expect(getTreeSitterBufferSize(1000)).toBe(TREE_SITTER_BUFFER_SIZE);
expect(getTreeSitterBufferSize('x'.repeat(100))).toBe(TREE_SITTER_BUFFER_SIZE);
expect(getTreeSitterBufferSize('')).toBe(TREE_SITTER_BUFFER_SIZE);
expect(getTreeSitterBufferSize('x'.repeat(1000))).toBe(TREE_SITTER_BUFFER_SIZE);
});
it('returns 2x content length when larger than minimum', () => {
const size = 400 * 1024; // 400 KB 2x = 800 KB > 512 KB min
expect(getTreeSitterBufferSize(size)).toBe(size * 2);
const size = 400 * 1024; // 400 KB, 2x = 800 KB > 512 KB min
expect(getTreeSitterBufferSize('x'.repeat(size))).toBe(size * 2);
});
it('caps at 32MB for very large files', () => {
const huge = 20 * 1024 * 1024; // 20 MB — 2x = 40 MB > 32 MB cap
const huge = 'x'.repeat(20 * 1024 * 1024); // 20 MB, 2x = 40 MB > 32 MB cap
expect(getTreeSitterBufferSize(huge)).toBe(32 * 1024 * 1024);
});
it('returns exactly 512KB at the boundary', () => {
// 256KB * 2 = 512KB = minimum, so should return minimum
expect(getTreeSitterBufferSize(256 * 1024)).toBe(TREE_SITTER_BUFFER_SIZE);
expect(getTreeSitterBufferSize('x'.repeat(256 * 1024))).toBe(TREE_SITTER_BUFFER_SIZE);
});
it('scales linearly between min and max', () => {
const small = getTreeSitterBufferSize(300 * 1024);
const medium = getTreeSitterBufferSize(1 * 1024 * 1024);
const large = getTreeSitterBufferSize(5 * 1024 * 1024);
const small = getTreeSitterBufferSize('x'.repeat(300 * 1024));
const medium = getTreeSitterBufferSize('x'.repeat(1 * 1024 * 1024));
const large = getTreeSitterBufferSize('x'.repeat(5 * 1024 * 1024));
expect(small).toBeLessThan(medium);
expect(medium).toBeLessThan(large);
});
it('sizes from UTF-8 bytes, not UTF-16 code units', () => {
const source = '漢'.repeat(190_000);
const byteLength = getTreeSitterContentByteLength(source);
expect(byteLength).toBe(source.length * 3);
expect(getTreeSitterBufferSize(source)).toBe(expectedBufferSize(byteLength));
});
it('caps UTF-8-heavy sources using byte length', () => {
const source = '漢'.repeat(6_000_000);
expect(getTreeSitterContentByteLength(source)).toBe(source.length * 3);
expect(getTreeSitterBufferSize(source)).toBe(TREE_SITTER_MAX_BUFFER);
});
it('TREE_SITTER_MAX_BUFFER is 32MB', () => {
expect(TREE_SITTER_MAX_BUFFER).toBe(32 * 1024 * 1024);
});
it('returns max buffer at exact boundary (16MB input)', () => {
// 16MB * 2 = 32MB = max
expect(getTreeSitterBufferSize(16 * 1024 * 1024)).toBe(TREE_SITTER_MAX_BUFFER);
expect(getTreeSitterBufferSize('x'.repeat(16 * 1024 * 1024))).toBe(TREE_SITTER_MAX_BUFFER);
});
it('file just over max returns max buffer', () => {
// 17MB * 2 = 34MB > 32MB cap
expect(getTreeSitterBufferSize(17 * 1024 * 1024)).toBe(TREE_SITTER_MAX_BUFFER);
expect(getTreeSitterBufferSize('x'.repeat(17 * 1024 * 1024))).toBe(TREE_SITTER_MAX_BUFFER);
});
it('handles files between old 512KB limit and new 32MB limit', () => {
// This is the range that was previously silently skipped
const sizes = [600 * 1024, 1024 * 1024, 5 * 1024 * 1024, 10 * 1024 * 1024];
for (const size of sizes) {
const bufSize = getTreeSitterBufferSize(size);
expect(bufSize).toBeGreaterThanOrEqual(TREE_SITTER_BUFFER_SIZE);
expect(bufSize).toBeLessThanOrEqual(TREE_SITTER_MAX_BUFFER);
expect(getTreeSitterBufferSize('x'.repeat(size))).toBe(expectedBufferSize(size));
}
});
});

View file

@ -26,6 +26,26 @@ describe('emitCsharpScopeCaptures — scopes', () => {
expect(all.some((t) => t.includes('@scope.module'))).toBe(true);
});
it('parses large cache-miss files with the adaptive tree-sitter buffer', () => {
const padding = 'x'.repeat(600 * 1024);
const match = findMatch(
`namespace Large;\n// ${padding}\nclass Big { public void AfterPadding() { } }`,
(t) => t.includes('@declaration.method'),
);
expect(match).toBeDefined();
expect(match!['@declaration.name'].text).toBe('AfterPadding');
});
it('parses UTF-8-heavy cache-miss files with a byte-sized buffer', () => {
const padding = '漢'.repeat(190_000);
const match = findMatch(
`namespace Large;\n// ${padding}\nclass Big { public void AfterPadding() { } }`,
(t) => t.includes('@declaration.method'),
);
expect(match).toBeDefined();
expect(match!['@declaration.name'].text).toBe('AfterPadding');
});
it('captures block-scoped namespaces as @scope.namespace', () => {
const all = tagsFor('namespace Foo.Bar { class A { } }');
expect(all.some((t) => t.includes('@scope.namespace'))).toBe(true);

View file

@ -18,16 +18,20 @@ import {
} from '../../../../src/core/ingestion/languages/csharp/simple-hooks.js';
import { csharpMergeBindings } from '../../../../src/core/ingestion/languages/csharp/merge-bindings.js';
import { csharpArityCompatibility } from '../../../../src/core/ingestion/languages/csharp/arity.js';
import { populateCsharpNamespaceSiblings } from '../../../../src/core/ingestion/languages/csharp/namespace-siblings.js';
import type {
BindingRef,
Callsite,
CaptureMatch,
ParsedFile,
ParsedImport,
Scope,
ScopeId,
ScopeTree,
SymbolDefinition,
TypeRef,
} from 'gitnexus-shared';
import type { ScopeResolutionIndexes } from '../../../../src/core/ingestion/model/scope-resolution-indexes.js';
function fakeScope(
kind: Scope['kind'],
@ -189,6 +193,137 @@ describe('csharpArityCompatibility', () => {
});
});
describe('populateCsharpNamespaceSiblings', () => {
const classDef = (nodeId: string, filePath: string, qualifiedName: string): SymbolDefinition =>
({ nodeId, filePath, qualifiedName, type: 'Class' }) as SymbolDefinition;
const scope = (
id: string,
kind: Scope['kind'],
filePath: string,
parent: ScopeId | null = null,
ownedDefs: readonly SymbolDefinition[] = [],
): Scope =>
({
id: id as ScopeId,
kind,
parent,
filePath,
range: { startLine: 1, startColumn: 0, endLine: 10, endColumn: 0 },
bindings: new Map(),
imports: [],
ownedDefs,
typeBindings: new Map(),
}) as unknown as Scope;
it('writes namespace siblings to the augmentation channel without touching frozen finalized bindings', () => {
// Verifies the post-finalize binding-augmentation contract for the
// C# namespace-siblings hook (per ScopeResolver I8 + the
// `bindingAugmentations` doc on `ScopeResolutionIndexes`):
// * `indexes.bindings` (the finalize output) stays frozen and
// its inner `BindingRef[]` arrays are NEVER mutated by the
// hook — proven here by passing a frozen bucket and asserting
// it survives unchanged.
// * Cross-file siblings are appended to
// `indexes.bindingAugmentations`, the dedicated mutable
// append-only buffer.
// * Walkers downstream (`lookupBindingsAt`) merge the two layers
// transparently — covered by walkers-augmentations.test.ts.
// Reproduces the pre-architecture `Cannot add property N, object
// is not extensible` crash by carrying a pre-frozen `BindingRef[]`
// through `indexes.bindings`. End-to-end coverage is in the
// `csharp-large-cache-miss-resolution` fixture.
const existing = classDef('def:external.B', 'external.cs', 'Other.B');
const sibling = classDef('def:b.B', 'b.cs', 'Demo.B');
const moduleA = scope('scope:a:module', 'Module', 'a.cs');
const moduleB = scope('scope:b:module', 'Module', 'b.cs');
const classB = scope('scope:b:class', 'Class', 'b.cs', moduleB.id, [sibling]);
const parsedFiles: ParsedFile[] = [
{
filePath: 'a.cs',
moduleScope: moduleA.id,
scopes: Object.freeze([moduleA]),
parsedImports: Object.freeze([]),
localDefs: Object.freeze([]),
referenceSites: Object.freeze([]),
} as ParsedFile,
{
filePath: 'b.cs',
moduleScope: moduleB.id,
scopes: Object.freeze([moduleB, classB]),
parsedImports: Object.freeze([]),
localDefs: Object.freeze([sibling]),
referenceSites: Object.freeze([]),
} as ParsedFile,
];
const frozenBucket = Object.freeze([{ def: existing, origin: 'import' } as BindingRef]);
const bindings = new Map<ScopeId, ReadonlyMap<string, readonly BindingRef[]>>([
[moduleA.id, new Map<string, readonly BindingRef[]>([['B', frozenBucket]])],
]);
const bindingAugmentations = new Map<ScopeId, ReadonlyMap<string, readonly BindingRef[]>>();
populateCsharpNamespaceSiblings(
parsedFiles,
{ bindings, bindingAugmentations } as unknown as ScopeResolutionIndexes,
{
fileContents: new Map([
['a.cs', 'namespace Demo;\nclass A { }\n'],
['b.cs', 'namespace Demo;\nclass B { }\n'],
]),
},
);
const finalized = bindings.get(moduleA.id)?.get('B') ?? [];
expect(finalized).toBe(frozenBucket);
expect(finalized.map((b) => b.def.nodeId)).toEqual(['def:external.B']);
expect(Object.isFrozen(finalized)).toBe(true);
const augmented = bindingAugmentations.get(moduleA.id)?.get('B') ?? [];
expect(augmented.map((b) => b.def.nodeId)).toEqual(['def:b.B']);
expect(Object.isFrozen(augmented)).toBe(false);
});
it('parses UTF-8-heavy cache-miss files before namespace sibling injection', () => {
const sibling = classDef('def:b.B', 'b.cs', 'Demo.B');
const moduleA = scope('scope:a:module', 'Module', 'a.cs');
const moduleB = scope('scope:b:module', 'Module', 'b.cs');
const classB = scope('scope:b:class', 'Class', 'b.cs', moduleB.id, [sibling]);
const parsedFiles: ParsedFile[] = [
{
filePath: 'a.cs',
moduleScope: moduleA.id,
scopes: Object.freeze([moduleA]),
parsedImports: Object.freeze([]),
localDefs: Object.freeze([]),
referenceSites: Object.freeze([]),
} as ParsedFile,
{
filePath: 'b.cs',
moduleScope: moduleB.id,
scopes: Object.freeze([moduleB, classB]),
parsedImports: Object.freeze([]),
localDefs: Object.freeze([sibling]),
referenceSites: Object.freeze([]),
} as ParsedFile,
];
const bindingAugmentations = new Map<ScopeId, ReadonlyMap<string, readonly BindingRef[]>>();
const padding = '漢'.repeat(190_000);
populateCsharpNamespaceSiblings(
parsedFiles,
{ bindings: new Map(), bindingAugmentations } as unknown as ScopeResolutionIndexes,
{
fileContents: new Map([
['a.cs', `namespace Demo;\n// ${padding}\nclass A { }\n`],
['b.cs', `namespace Demo;\n// ${padding}\nclass B { }\n`],
]),
},
);
expect(bindingAugmentations.get(moduleA.id)?.get('B')?.[0]?.def.nodeId).toBe('def:b.B');
});
});
describe('csharpReceiverBinding', () => {
it('returns the `this` type binding for an instance method scope', () => {
const binding: TypeRef = { rawName: 'User', source: 'self' } as unknown as TypeRef;

View file

@ -30,7 +30,16 @@ import { typescriptScopeResolver } from '../../../src/core/ingestion/languages/t
import { finalizeScopeModel } from '../../../src/core/ingestion/finalize-orchestrator.js';
import { buildWorkspaceResolutionIndex } from '../../../src/core/ingestion/scope-resolution/workspace-index.js';
import { propagateImportedReturnTypes } from '../../../src/core/ingestion/scope-resolution/passes/imported-return-types.js';
import type { ParsedFile } from 'gitnexus-shared';
import type {
BindingRef,
ParsedFile,
Scope,
ScopeId,
ScopeTree,
SymbolDefinition,
} from 'gitnexus-shared';
import type { ScopeResolutionIndexes } from '../../../src/core/ingestion/model/scope-resolution-indexes.js';
import type { WorkspaceResolutionIndex } from '../../../src/core/ingestion/scope-resolution/workspace-index.js';
interface InMemoryFile {
readonly path: string;
@ -197,6 +206,61 @@ import { helper } from './service';
}
});
it('mirrors import return types from bindingAugmentations-only refs', () => {
const appScopeId = 'scope:app' as ScopeId;
const sourceScopeId = 'scope:source' as ScopeId;
const appModule = {
id: appScopeId,
kind: 'Module',
parent: null,
filePath: 'app.ts',
bindings: new Map(),
typeBindings: new Map(),
} as unknown as Scope;
const sourceModule = {
id: sourceScopeId,
kind: 'Module',
parent: null,
filePath: 'source.ts',
bindings: new Map(),
typeBindings: new Map([['getUser', { rawName: 'User', source: 'return-annotation' }]]),
} as unknown as Scope;
const importedDef = {
nodeId: 'def:source.getUser',
filePath: 'source.ts',
qualifiedName: 'getUser',
type: 'Function',
} as SymbolDefinition;
const scopeTree = {
getScope: (id: ScopeId) => {
if (id === appScopeId) return appModule;
if (id === sourceScopeId) return sourceModule;
return undefined;
},
} as unknown as ScopeTree;
const indexes = {
scopeTree,
bindings: new Map(),
bindingAugmentations: new Map([
[
appScopeId,
new Map([['getUser', [{ def: importedDef, origin: 'import' } as BindingRef]]]),
],
]),
sccs: [{ files: ['app.ts'] }],
} as unknown as ScopeResolutionIndexes;
const workspaceIndex = {
moduleScopeByFile: new Map([
['app.ts', appModule],
['source.ts', sourceModule],
]),
} as unknown as WorkspaceResolutionIndex;
propagateImportedReturnTypes([], indexes, workspaceIndex);
expect(appModule.typeBindings.get('getUser')?.rawName).toBe('User');
});
it('does not throw on a cyclic SCC (partial fixpoint, best-effort)', () => {
// a.ts imports from b, b.ts imports from a. The two files form
// a single cyclic SCC. Within one pass we mirror what we can;

View file

@ -65,6 +65,20 @@ describe('Python scopes — module / class / function', () => {
expect(f.scopes[0]!.kind).toBe('Module');
});
it('case 01b: large cache-miss files use the adaptive tree-sitter buffer', () => {
const padding = 'x'.repeat(600 * 1024);
const f = parse(`# ${padding}\ndef after_padding():\n return 1\n`);
expect(scopesByKind(f, 'Module')).toHaveLength(1);
expect(findDef(f, 'after_padding')?.type).toBe('Function');
});
it('case 01c: UTF-8-heavy cache-miss files use byte-sized parser buffers', () => {
const padding = '漢'.repeat(190_000);
const f = parse(`# ${padding}\ndef after_padding():\n return 1\n`);
expect(scopesByKind(f, 'Module')).toHaveLength(1);
expect(findDef(f, 'after_padding')?.type).toBe('Function');
});
it('case 02: module-level assignment produces a Variable declaration in Module scope', () => {
const f = parse('x = 1\n');
expect(scopesByKind(f, 'Module')).toHaveLength(1);

View file

@ -37,6 +37,24 @@ describe('emitTsScopeCaptures — scopes', () => {
expect(all.some((t) => t.includes('@scope.module'))).toBe(true);
});
it('parses large cache-miss files with the adaptive tree-sitter buffer', () => {
const padding = 'x'.repeat(600 * 1024);
const match = findMatch(`// ${padding}\nclass Big { afterPadding(): void {} }`, (t) =>
t.includes('@declaration.method'),
);
expect(match).toBeDefined();
expect(match!['@declaration.name'].text).toBe('afterPadding');
});
it('parses UTF-8-heavy cache-miss files with a byte-sized buffer', () => {
const padding = '漢'.repeat(190_000);
const match = findMatch(`// ${padding}\nclass Big { afterPadding(): void {} }`, (t) =>
t.includes('@declaration.method'),
);
expect(match).toBeDefined();
expect(match!['@declaration.name'].text).toBe('afterPadding');
});
it('captures internal_module as @scope.namespace', () => {
const all = tagsFor('namespace Foo { class A { } }');
expect(all.some((t) => t.includes('@scope.namespace'))).toBe(true);

View file

@ -0,0 +1,177 @@
/**
* Unit tests for the dev-mode I8 binding-immutability validator.
*
* Mirrors `validateOwnershipParity` (#909) happy path + drift
* detection + opt-in runtime gating. Pinning these so a
* future contributor can't silently re-introduce the issue #1066
* shape (a hook mutating `indexes.bindings` instead of
* `indexes.bindingAugmentations`) without tripping the validator.
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import type { BindingRef, ScopeId } from 'gitnexus-shared';
import type { ScopeResolutionIndexes } from '../../../src/core/ingestion/model/scope-resolution-indexes.js';
import { validateBindingsImmutability } from '../../../src/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js';
const mkRef = (nodeId: string): BindingRef =>
({
def: { nodeId, filePath: 'x.ts', type: 'Class' },
origin: 'local',
}) as unknown as BindingRef;
const mkIndexes = (
bindings: Map<ScopeId, Map<string, readonly BindingRef[]>>,
augmentations: Map<ScopeId, Map<string, BindingRef[]>>,
): ScopeResolutionIndexes =>
({
bindings,
bindingAugmentations: augmentations,
}) as unknown as ScopeResolutionIndexes;
describe('validateBindingsImmutability', () => {
beforeEach(() => {
// Insulate against an ambient VALIDATE_SEMANTIC_MODEL in a developer's
// shell. Per-test env tweaks override this baseline as needed.
vi.stubEnv('VALIDATE_SEMANTIC_MODEL', undefined);
});
afterEach(() => {
vi.unstubAllEnvs();
});
it('is silent when finalized buckets are frozen and augmentation buckets are mutable', () => {
vi.stubEnv('NODE_ENV', 'development');
const bindings = new Map<ScopeId, Map<string, readonly BindingRef[]>>([
['scope:a:module', new Map([['Foo', Object.freeze([mkRef('def:Foo')])]])],
]);
const augmentations = new Map<ScopeId, Map<string, BindingRef[]>>([
['scope:a:module', new Map([['Bar', [mkRef('def:Bar')]]])],
]);
const onWarn = vi.fn();
const violations = validateBindingsImmutability(mkIndexes(bindings, augmentations), onWarn);
expect(violations).toBe(0);
expect(onWarn).not.toHaveBeenCalled();
});
it('warns when a bucket in indexes.bindings is NOT frozen', () => {
vi.stubEnv('NODE_ENV', 'development');
const bindings = new Map<ScopeId, Map<string, readonly BindingRef[]>>([
['scope:a:module', new Map([['Foo', [mkRef('def:Foo')] as readonly BindingRef[]]])],
]);
const augmentations = new Map<ScopeId, Map<string, BindingRef[]>>();
const onWarn = vi.fn();
const violations = validateBindingsImmutability(mkIndexes(bindings, augmentations), onWarn);
expect(violations).toBe(1);
expect(onWarn).toHaveBeenCalledTimes(1);
expect(onWarn.mock.calls[0][0]).toMatch(/binding-immutability/);
expect(onWarn.mock.calls[0][0]).toMatch(/indexes\.bindings/);
expect(onWarn.mock.calls[0][0]).toMatch(/I8/);
});
it('warns when a bucket in indexes.bindingAugmentations IS frozen', () => {
vi.stubEnv('NODE_ENV', 'development');
const bindings = new Map<ScopeId, Map<string, readonly BindingRef[]>>();
const augmentations = new Map<ScopeId, Map<string, BindingRef[]>>([
['scope:a:module', new Map([['Bar', Object.freeze([mkRef('def:Bar')]) as BindingRef[]]])],
]);
const onWarn = vi.fn();
const violations = validateBindingsImmutability(mkIndexes(bindings, augmentations), onWarn);
expect(violations).toBe(1);
expect(onWarn).toHaveBeenCalledTimes(1);
expect(onWarn.mock.calls[0][0]).toMatch(/binding-immutability/);
expect(onWarn.mock.calls[0][0]).toMatch(/indexes\.bindingAugmentations/);
expect(onWarn.mock.calls[0][0]).toMatch(/I8/);
});
it('does not detect semantically wrong frozen replacements in indexes.bindings', () => {
vi.stubEnv('NODE_ENV', 'development');
const bindings = new Map<ScopeId, Map<string, readonly BindingRef[]>>([
['scope:a:module', new Map([['Foo', Object.freeze([mkRef('def:Wrong')])]])],
]);
const augmentations = new Map<ScopeId, Map<string, BindingRef[]>>();
const onWarn = vi.fn();
const violations = validateBindingsImmutability(mkIndexes(bindings, augmentations), onWarn);
expect(violations).toBe(0);
expect(onWarn).not.toHaveBeenCalled();
});
it('counts violations across multiple scopes', () => {
vi.stubEnv('NODE_ENV', 'development');
const bindings = new Map<ScopeId, Map<string, readonly BindingRef[]>>([
['scope:a:module', new Map([['Foo', [mkRef('def:Foo')] as readonly BindingRef[]]])],
['scope:b:module', new Map([['Bar', [mkRef('def:Bar')] as readonly BindingRef[]]])],
]);
const augmentations = new Map<ScopeId, Map<string, BindingRef[]>>();
const onWarn = vi.fn();
const violations = validateBindingsImmutability(mkIndexes(bindings, augmentations), onWarn);
expect(violations).toBe(2);
expect(onWarn).toHaveBeenCalledTimes(2);
});
it('is a no-op when NODE_ENV=production', () => {
vi.stubEnv('NODE_ENV', 'production');
const bindings = new Map<ScopeId, Map<string, readonly BindingRef[]>>([
['scope:a:module', new Map([['Foo', [mkRef('def:Foo')] as readonly BindingRef[]]])],
]);
const augmentations = new Map<ScopeId, Map<string, BindingRef[]>>();
const onWarn = vi.fn();
const violations = validateBindingsImmutability(mkIndexes(bindings, augmentations), onWarn);
expect(violations).toBe(0);
expect(onWarn).not.toHaveBeenCalled();
});
it('is a no-op in default CLI env when NODE_ENV is unset', () => {
vi.stubEnv('NODE_ENV', undefined);
const bindings = new Map<ScopeId, Map<string, readonly BindingRef[]>>([
['scope:a:module', new Map([['Foo', [mkRef('def:Foo')] as readonly BindingRef[]]])],
]);
const augmentations = new Map<ScopeId, Map<string, BindingRef[]>>();
const onWarn = vi.fn();
const violations = validateBindingsImmutability(mkIndexes(bindings, augmentations), onWarn);
expect(violations).toBe(0);
expect(onWarn).not.toHaveBeenCalled();
});
it('runs when VALIDATE_SEMANTIC_MODEL=1 even if NODE_ENV is unset', () => {
vi.stubEnv('NODE_ENV', undefined);
vi.stubEnv('VALIDATE_SEMANTIC_MODEL', '1');
const bindings = new Map<ScopeId, Map<string, readonly BindingRef[]>>([
['scope:a:module', new Map([['Foo', [mkRef('def:Foo')] as readonly BindingRef[]]])],
]);
const augmentations = new Map<ScopeId, Map<string, BindingRef[]>>();
const onWarn = vi.fn();
const violations = validateBindingsImmutability(mkIndexes(bindings, augmentations), onWarn);
expect(violations).toBe(1);
expect(onWarn).toHaveBeenCalledTimes(1);
});
it('is a no-op when VALIDATE_SEMANTIC_MODEL=0', () => {
vi.stubEnv('NODE_ENV', 'development');
vi.stubEnv('VALIDATE_SEMANTIC_MODEL', '0');
const bindings = new Map<ScopeId, Map<string, readonly BindingRef[]>>([
['scope:a:module', new Map([['Foo', [mkRef('def:Foo')] as readonly BindingRef[]]])],
]);
const augmentations = new Map<ScopeId, Map<string, BindingRef[]>>();
const onWarn = vi.fn();
const violations = validateBindingsImmutability(mkIndexes(bindings, augmentations), onWarn);
expect(violations).toBe(0);
expect(onWarn).not.toHaveBeenCalled();
});
});

View file

@ -0,0 +1,195 @@
/**
* Unit coverage for `lookupBindingsAt` the dual-source binding
* lookup primitive used by every walker that needs cross-file
* visibility (Step 2 of the binding-augmentation-channel refactor).
*
* These tests pin the contract exhaustively: precedence (finalized
* first), dedup (by `def.nodeId`), empty-array semantics, and the
* shared-empty-frozen-array identity for misses. Every other walker
* test in this directory delegates to `lookupBindingsAt` after the
* refactor, so a regression here surfaces quickly.
*/
import { describe, it, expect } from 'vitest';
import {
findCallableBindingInScope,
findClassBindingInScope,
findExportedDefByName,
lookupBindingsAt,
} from '../../../src/core/ingestion/scope-resolution/scope/walkers.js';
import type { BindingRef, Scope, ScopeId, ScopeTree, SymbolDefinition } from 'gitnexus-shared';
import type { ScopeResolutionIndexes } from '../../../src/core/ingestion/model/scope-resolution-indexes.js';
import type { WorkspaceResolutionIndex } from '../../../src/core/ingestion/scope-resolution/workspace-index.js';
const SCOPE = 'scope:m' as ScopeId;
const def = (nodeId: string): SymbolDefinition =>
({ nodeId, filePath: 'm.ts', type: 'Function' }) as SymbolDefinition;
const ref = (nodeId: string, origin: BindingRef['origin'] = 'local'): BindingRef =>
({ def: def(nodeId), origin }) as BindingRef;
function indexesWith({
finalized,
augmented,
}: {
finalized?: readonly BindingRef[];
augmented?: readonly BindingRef[];
}): ScopeResolutionIndexes {
const bindings = new Map<ScopeId, Map<string, readonly BindingRef[]>>();
if (finalized !== undefined) {
Object.freeze(finalized as BindingRef[]);
bindings.set(SCOPE, new Map([['name', finalized]]));
}
const bindingAugmentations = new Map<ScopeId, Map<string, readonly BindingRef[]>>();
if (augmented !== undefined) bindingAugmentations.set(SCOPE, new Map([['name', augmented]]));
return { bindings, bindingAugmentations } as unknown as ScopeResolutionIndexes;
}
function scope(id: ScopeId, bindings = new Map<string, readonly BindingRef[]>()): Scope {
return {
id,
kind: 'Module',
parent: null,
filePath: 'm.ts',
range: { startLine: 1, startColumn: 0, endLine: 1, endColumn: 0 },
bindings,
imports: [],
ownedDefs: [],
typeBindings: new Map(),
} as unknown as Scope;
}
function indexesForScopeLookup(
moduleScope: Scope,
augmented: Map<string, readonly BindingRef[]>,
): ScopeResolutionIndexes {
const scopeTree = {
getScope: (id: ScopeId) => (id === moduleScope.id ? moduleScope : undefined),
} as unknown as ScopeTree;
return {
scopeTree,
bindings: new Map(),
bindingAugmentations: new Map([[moduleScope.id, augmented]]),
} as unknown as ScopeResolutionIndexes;
}
describe('lookupBindingsAt', () => {
it('returns the finalized bucket when augmentations are absent', () => {
const finalized = [ref('A'), ref('B')];
const out = lookupBindingsAt(SCOPE, 'name', indexesWith({ finalized }));
expect(out).toEqual(finalized);
// Identity preserved when only one channel populates — no allocation.
expect(out).toBe(finalized);
});
it('returns the augmented bucket when finalized is absent', () => {
const augmented = [ref('X', 'namespace')];
const out = lookupBindingsAt(SCOPE, 'name', indexesWith({ augmented }));
expect(out).toEqual(augmented);
expect(out).toBe(augmented);
});
it('concatenates with finalized first when both populate disjoint nodeIds', () => {
const finalized = [ref('A', 'import'), ref('B', 'import')];
const augmented = [ref('C', 'namespace'), ref('D', 'namespace')];
const out = lookupBindingsAt(SCOPE, 'name', indexesWith({ finalized, augmented }));
expect(out.map((b) => b.def.nodeId)).toEqual(['A', 'B', 'C', 'D']);
});
it('dedupes augmented entries that share a nodeId with finalized (finalized wins)', () => {
const finalized = [ref('A', 'import'), ref('B', 'import')];
const augmented = [ref('A', 'namespace'), ref('C', 'namespace')];
const out = lookupBindingsAt(SCOPE, 'name', indexesWith({ finalized, augmented }));
expect(out.map((b) => b.def.nodeId)).toEqual(['A', 'B', 'C']);
expect(out.find((b) => b.def.nodeId === 'A')!.origin).toBe('import');
});
it('keeps finalized metadata when the same nodeId appears in both channels', () => {
const finalizedDef = {
nodeId: 'A',
filePath: 'finalized.ts',
qualifiedName: 'finalized.A',
type: 'Function',
} as SymbolDefinition;
const augmentedDef = {
nodeId: 'A',
filePath: 'augmented.ts',
qualifiedName: 'augmented.A',
type: 'Method',
} as SymbolDefinition;
const out = lookupBindingsAt(
SCOPE,
'name',
indexesWith({
finalized: [{ def: finalizedDef, origin: 'import' } as BindingRef],
augmented: [{ def: augmentedDef, origin: 'namespace' } as BindingRef],
}),
);
expect(out).toHaveLength(1);
expect(out[0]!.def.filePath).toBe('finalized.ts');
expect(out[0]!.def.qualifiedName).toBe('finalized.A');
expect(out[0]!.origin).toBe('import');
});
it('returns the shared empty array on a miss in both channels', () => {
const a = lookupBindingsAt(SCOPE, 'name', indexesWith({}));
const b = lookupBindingsAt(SCOPE, 'other', indexesWith({}));
expect(a).toEqual([]);
expect(b).toEqual([]);
expect(a).toBe(b);
expect(Object.isFrozen(a)).toBe(true);
});
it('treats an empty finalized bucket as absent (returns augmented)', () => {
const augmented = [ref('Z', 'namespace')];
const out = lookupBindingsAt(SCOPE, 'name', indexesWith({ finalized: [], augmented }));
expect(out).toBe(augmented);
});
it('treats an empty augmented bucket as absent (returns finalized)', () => {
const finalized = [ref('Z', 'import')];
const out = lookupBindingsAt(SCOPE, 'name', indexesWith({ finalized, augmented: [] }));
expect(out).toBe(finalized);
});
it('returns the shared empty array when both buckets exist but are empty', () => {
const out = lookupBindingsAt(SCOPE, 'name', indexesWith({ finalized: [], augmented: [] }));
expect(out).toEqual([]);
expect(Object.isFrozen(out)).toBe(true);
});
});
describe('walker helpers read bindingAugmentations', () => {
it('findClassBindingInScope finds class-like refs that exist only in augmentations', () => {
const moduleScope = scope(SCOPE);
const classRef = {
def: { ...def('ClassA'), type: 'Class' },
origin: 'namespace',
} as BindingRef;
const indexes = indexesForScopeLookup(moduleScope, new Map([['ClassA', [classRef]]]));
expect(findClassBindingInScope(SCOPE, 'ClassA', indexes)?.nodeId).toBe('ClassA');
});
it('findCallableBindingInScope finds callable refs that exist only in augmentations', () => {
const moduleScope = scope(SCOPE);
const callableRef = { def: def('callMe'), origin: 'import' } as BindingRef;
const indexes = indexesForScopeLookup(moduleScope, new Map([['callMe', [callableRef]]]));
expect(findCallableBindingInScope(SCOPE, 'callMe', indexes)?.nodeId).toBe('callMe');
});
it('findExportedDefByName finds callable refs that exist only in augmentations', () => {
const moduleScope = scope(SCOPE);
const callableRef = { def: def('fromAugmentation'), origin: 'import' } as BindingRef;
const indexes = indexesForScopeLookup(moduleScope, new Map([['run', [callableRef]]]));
const workspaceIndex = {
moduleScopeByFile: new Map(),
} as unknown as WorkspaceResolutionIndex;
expect(findExportedDefByName('run', SCOPE, indexes, workspaceIndex)?.nodeId).toBe(
'fromAugmentation',
);
});
});