mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-03 02:21:44 +00:00
* feat(java): expand wildcard static imports + POSIX-space import resolution - `import static a.b.C.*` records the class FQN at extract time (ModuleConstants.wildcardImports) and expandJavaWildcardStaticImports materializes the bare-name bindings once the repo constants map exists, mirroring explicit single-member static imports. Wired in both the group-side prepareRepo fold and the ingestion-side parse-impl pass so the two query surfaces cannot diverge. Without this, wildcard-imported route constants (~693 routes on our monorepo) silently failed to fold and their provider contracts were dropped. - resolveJavaImport compares in POSIX space (backslash-normalized repo keys) — on Windows the '/'-joined class file never matched a backslash-keyed repo (observed: 675 calls, zero hits). - Wildcard bindings never overwrite single imports (a member shadowing its own wildcard is honored); unresolved wildcards degrade to the existing skip floor. Rebased onto current main: the parse-worker gate this originally carried is superseded by the provider moduleConstantHeuristic architecture; only the resolver-side wildcard expansion and POSIX tolerance remain. * chore(cache): claim SCHEMA_BUMP 83 (82 taken upstream by Spring lookup facts) * style: prettier * fix(java): make wildcard static imports actually resolve route constants extractJavaModuleConstants never populated wildcardImports, so `import static a.b.C.*;` was inert: the asterisk is a sibling of scoped_identifier in tree-sitter-java, not a path segment. Record the class FQN there and stop binding the class simple name as a field. Wildcard-only files also fell through every harvest gate (the java provider heuristic regex, the parse worker emit check, and the group prepareRepo filter), so the constants never reached either layer. Expansion now resolves targets against constant-defining files only, keeping ingestion and group in parity (#2980 R4). Adds unit coverage for extraction, expansion/shadowing, the unresolved skip floor, the harvest heuristic, Windows path keys, and group <-> ingestion parity. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(kotlin): fold package-star imports for route constants Kotlin `import pkg.*` now records star scopes and resolves unique top-level names after local and explicit imports, matching the Java wildcard path without accepting invalid object-star imports. Reuse a Java constant-file suffix index across expansion so wildcard materialization stays linear as both constants and importers scale. Add named-vs-star benches and CI --check gates. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(jvm): fold wildcard imports without shadowing locals or skipping same-package names Keep Java unfoldable declarations from being resurrected by static wildcards, bind only the target type's members, and prefer Kotlin same-package names over package-star imports. Move repo-wide preparation behind a language-provider hook so the shared parse phase stays language-agnostic. Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3110) - Stop treating a Java static wildcard as a type import for qualified refs. - Harvest only class and static (including on-demand) imports, not import pkg.*. - Let harvested Kotlin top-level names shadow same-package star imports. Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3110) - Fold Kotlin classifier-star imports (`import Type.*`) the same way package stars already fold. Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3110) - Rebuild the Kotlin constant index when overlay replaces a contributing file. - Document the wildcard shape in the Java pipeline e2e fixture. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: l.cx <l.cx@winning.com.cn> Co-authored-by: ChunxueLi <mecoloud@users.noreply.gitee.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
158 lines
4.7 KiB
JavaScript
158 lines
4.7 KiB
JavaScript
/**
|
|
* Build-free throughput + identity benchmark for Java wildcard-static route constants.
|
|
*
|
|
* Arms:
|
|
* - named: explicit `import static ...ApiPaths.ROUTE_n` control
|
|
* - wildcard: `import static ...ApiPaths.*` feature path
|
|
*
|
|
* Parsing is prepared outside the timer. The measured path mirrors ingestion:
|
|
* build the constant-key index once, materialize pending wildcard imports, then
|
|
* read the resulting binding. Route folding itself has separate integration
|
|
* coverage and an older per-fold index cost shared by both arms.
|
|
*
|
|
* Usage:
|
|
* node --import tsx bench/java-wildcard-route-constants/measure.mjs
|
|
* node --import tsx bench/java-wildcard-route-constants/measure.mjs --check
|
|
*/
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import Parser from 'tree-sitter';
|
|
import Java from 'tree-sitter-java';
|
|
import {
|
|
extractJavaModuleConstants,
|
|
prepareJavaRouteConstants,
|
|
} from '../../src/core/ingestion/route-extractors/java-const-resolver.ts';
|
|
import {
|
|
fingerprintIds,
|
|
minSampleFresh,
|
|
runBaselineCheck,
|
|
runCountCheck,
|
|
runFingerprintParityCheck,
|
|
} from '../lib/route-constant-guard.mjs';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const BASELINE_PATH = path.resolve(__dirname, 'baselines.json');
|
|
const SMALL = 250;
|
|
const LARGE = 800;
|
|
const REPS = 15;
|
|
const WARMUP = 5;
|
|
|
|
const parser = new Parser();
|
|
parser.setLanguage(Java);
|
|
|
|
function constantsSource(i) {
|
|
return `package bench.constants;
|
|
public final class ApiPaths${i} {
|
|
public static final String ROUTE = "/api/routes/${i}";
|
|
}
|
|
`;
|
|
}
|
|
|
|
function controllerSource(i, mode) {
|
|
const fqn = `bench.constants.ApiPaths${i}`;
|
|
const imported = mode === 'wildcard' ? `import static ${fqn}.*;` : `import static ${fqn}.ROUTE;`;
|
|
return `package bench.web;
|
|
${imported}
|
|
class Controller${i} {}
|
|
`;
|
|
}
|
|
|
|
function cloneConstants(mc) {
|
|
return {
|
|
literals: new Map(mc.literals),
|
|
exprs: new Map(mc.exprs),
|
|
imports: new Map(mc.imports),
|
|
wildcardImports: mc.wildcardImports ? [...mc.wildcardImports] : undefined,
|
|
unfoldableDeclarations: new Set(mc.unfoldableDeclarations ?? []),
|
|
};
|
|
}
|
|
|
|
function prepare(mode, fileCount) {
|
|
const constants = [];
|
|
const controllers = [];
|
|
for (let i = 0; i < fileCount; i++) {
|
|
constants.push({
|
|
key: `bench/constants/ApiPaths${i}.java`,
|
|
constants: extractJavaModuleConstants(parser.parse(constantsSource(i))),
|
|
});
|
|
controllers.push({
|
|
key: `bench/web/Controller${i}.java`,
|
|
route: 'ROUTE',
|
|
constants: extractJavaModuleConstants(parser.parse(controllerSource(i, mode))),
|
|
});
|
|
}
|
|
return { constants, controllers };
|
|
}
|
|
|
|
function instantiate(prepared) {
|
|
const repo = new Map();
|
|
for (const constant of prepared.constants) {
|
|
repo.set(constant.key, cloneConstants(constant.constants));
|
|
}
|
|
const controllers = [];
|
|
for (const controller of prepared.controllers) {
|
|
repo.set(controller.key, cloneConstants(controller.constants));
|
|
controllers.push({ key: controller.key, route: controller.route });
|
|
}
|
|
return { repo, controllers };
|
|
}
|
|
|
|
function runAll(instance) {
|
|
const { repo, controllers } = instance;
|
|
prepareJavaRouteConstants(repo);
|
|
const bindings = [];
|
|
for (const controller of controllers) {
|
|
const mc = repo.get(controller.key);
|
|
const binding = mc.imports.get(controller.route);
|
|
if (binding) {
|
|
bindings.push(
|
|
`${controller.key}:${controller.route}:${binding.module}:${binding.originalName}`,
|
|
);
|
|
}
|
|
}
|
|
return bindings;
|
|
}
|
|
|
|
function measure(mode, fileCount) {
|
|
const prepared = prepare(mode, fileCount);
|
|
// Expansion mutates each importing file's `imports` map. Give every timed
|
|
// sample a fresh repo, but build those clones outside the timer.
|
|
const { last, ms } = minSampleFresh(() => instantiate(prepared), runAll, WARMUP, REPS);
|
|
return {
|
|
files: fileCount,
|
|
ms,
|
|
bindings: last.length,
|
|
fingerprint: fingerprintIds(last),
|
|
};
|
|
}
|
|
|
|
const report = {
|
|
named_small: measure('named', SMALL),
|
|
named_large: measure('named', LARGE),
|
|
wildcard_small: measure('wildcard', SMALL),
|
|
wildcard_large: measure('wildcard', LARGE),
|
|
};
|
|
report.scaling_ratio = Number(
|
|
(report.wildcard_large.ms / report.wildcard_small.ms / (LARGE / SMALL)).toFixed(3),
|
|
);
|
|
report.overhead_us_per_binding = Number(
|
|
(
|
|
((report.wildcard_large.ms - report.named_large.ms) * 1000) /
|
|
report.wildcard_large.bindings
|
|
).toFixed(3),
|
|
);
|
|
report.absolute_ms = report.wildcard_large.ms;
|
|
report.fingerprint = report.wildcard_large.fingerprint;
|
|
|
|
runCountCheck(report, 'bindings', {
|
|
named_large: LARGE,
|
|
wildcard_large: LARGE,
|
|
});
|
|
runFingerprintParityCheck(report, 'named_large', 'wildcard_large');
|
|
|
|
if (!process.argv.includes('--check')) {
|
|
console.log(JSON.stringify(report, null, 2));
|
|
process.exit(0);
|
|
}
|
|
|
|
runBaselineCheck(report, BASELINE_PATH);
|