feat(kernel): R4 — Java port with Lombok synthesis, gate passed, default-on

Java joins the native kernel (codegraph-kernel/src/java.rs), mirroring
the wasm extractor's Java paths bug-for-bug: package namespaces,
imports, javadoc, annotations→decorates, type_list inheritance,
static-final constants, enum constants, anonymous classes (including
the TS side's 0-based-line quirk on the extends ref), method_invocation
calls with the this.field unwrap and the Foo.getInstance().bar() chain
encoding (#645/#608), static-member value reads, method-reference
fn-refs (#756), value-reference edges, and the full Lombok member
synthesizer (#912: Getter/Setter/Data/Value/Builder/ToString/
EqualsAndHashCode/Slf4j-family with taken-member dedup). The shared
docstring/textutil modules moved to crate level. Grammar:
tree-sitter-java 0.23.5, with the wasm grammar vendored from the same
tag (parser.c sha-matched) replacing tree-sitter-wasms' 2023-era build.

Gate (plan §4c): extraction sweeps 100% — gson 262/262, retrofit
341/341, dubbo 4,048/4,048 — plus a Java torture fixture in npm test;
full-init dump-diffs byte-identical on gson (49,766 rows), retrofit
(62,735), and dubbo (441,266 rows); all R2/R3 repos re-verified; Linux
container runs all 23 kernel tests green under CODEGRAPH_KERNEL_EXPECT=1.

The gate caught a real cross-language bug: fn-ref dedupe and value-ref
self-target checks must compare node ID STRINGS, not node-table rows —
ids collide for same-(kind, name, line) nodes, which minified one-line
bundles hit routinely (retrofit's website JS exposed it; latent in the
TS/JS walker since R2, never released). Fixed in both walkers.

Benchmark honesty: dubbo fresh-init on an 11-core Mac is ~flat
(parse-loop wall 5,020→4,394ms; total ~11.3s both arms) because that
wall is main-thread-bound (reads + store), not worker-CPU-bound — the
§6 expectation assumed otherwise. Where worker CPU binds the kernel
delivers: dubbo on a 2-CPU/6GB container drops 27.8-28.6s → 22.3-22.8s
(~1.25×). The identified lever for the many-core headline is decoding
kernel buffers directly into store rows (skipping per-node JS object
materialization); the buffer contract already carries everything.

DEFAULT_ROUTED now includes java. Full suite: 2,467 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Colby McHenry
2026-07-16 23:25:29 -05:00
co-authored by Claude Fable 5
parent c8cca9a601
commit 03d54e47a1
19 changed files with 1828 additions and 30 deletions
+140
View File
@@ -0,0 +1,140 @@
//! getPrecedingDocstring / cleanCommentMarkers — faithful port of
//! src/extraction/tree-sitter-helpers.ts (#780 wrapper-climb semantics).
use regex::Regex;
use std::sync::OnceLock;
use tree_sitter::Node;
/// DOCSTRING_WRAPPER_TYPES (tree-sitter-helpers.ts).
fn is_wrapper(kind: &str) -> bool {
matches!(
kind,
"export_statement"
| "decorated_definition"
| "lexical_declaration"
| "variable_declaration"
| "variable_declarator"
| "ambient_declaration"
)
}
fn is_comment(kind: &str) -> bool {
matches!(
kind,
"comment" | "line_comment" | "block_comment" | "documentation_comment"
)
}
struct Cleaners {
block_open: Regex,
block_close: Regex,
lua_open: Regex,
lua_close: Regex,
paren_star_open: Regex,
paren_star_close: Regex,
brace_open: Regex,
brace_close: Regex,
slashes: Regex,
dashes: Regex,
hash: Regex,
percent: Regex,
star_cont: Regex,
}
fn cleaners() -> &'static Cleaners {
static C: OnceLock<Cleaners> = OnceLock::new();
C.get_or_init(|| Cleaners {
block_open: Regex::new(r"^/\*+!?").unwrap(),
block_close: Regex::new(r"\*+/$").unwrap(),
lua_open: Regex::new(r"^--\[=*\[").unwrap(),
lua_close: Regex::new(r"\]=*\]$").unwrap(),
paren_star_open: Regex::new(r"^\(\*").unwrap(),
paren_star_close: Regex::new(r"\*\)$").unwrap(),
brace_open: Regex::new(r"^\{").unwrap(),
brace_close: Regex::new(r"\}$").unwrap(),
slashes: Regex::new(r"(?m)^//[/!]?\s?").unwrap(),
dashes: Regex::new(r"(?m)^--\s?").unwrap(),
hash: Regex::new(r"(?m)^#\s?").unwrap(),
percent: Regex::new(r"(?m)^%+\s?").unwrap(),
star_cont: Regex::new(r"(?m)^\s*\*\s?").unwrap(),
})
}
/// cleanCommentMarkers — strip comment syntax, keep the prose.
pub fn clean_comment_markers(comment: &str) -> String {
let c = cleaners();
let mut s = comment.trim().to_string();
if s.starts_with("/*") {
s = c.block_open.replace(&s, "").into_owned();
s = c.block_close.replace(&s, "").into_owned();
} else if s.starts_with("--[") {
s = c.lua_open.replace(&s, "").into_owned();
s = c.lua_close.replace(&s, "").into_owned();
} else if s.starts_with("(*") {
s = c.paren_star_open.replace(&s, "").into_owned();
s = c.paren_star_close.replace(&s, "").into_owned();
} else if s.starts_with('{') {
s = c.brace_open.replace(&s, "").into_owned();
s = c.brace_close.replace(&s, "").into_owned();
}
s = c.slashes.replace_all(&s, "").into_owned();
s = c.dashes.replace_all(&s, "").into_owned();
s = c.hash.replace_all(&s, "").into_owned();
s = c.percent.replace_all(&s, "").into_owned();
s = c.star_cont.replace_all(&s, "").into_owned();
s.trim().to_string()
}
/// getPrecedingDocstring — collect the comment run immediately preceding the
/// node (climbing out of declaration wrappers first), cleaned and joined.
/// Returns None when there is no preceding comment (a PRESENT-but-empty
/// docstring after cleaning still returns Some(""), matching the TS helper).
pub fn preceding_docstring(node: Node, src: &str) -> Option<String> {
let mut anchor = node;
while let Some(parent) = anchor.parent() {
if is_wrapper(parent.kind()) {
anchor = parent;
} else {
break;
}
}
let mut comments: Vec<&str> = Vec::new();
let mut sibling = anchor.prev_named_sibling();
while let Some(s) = sibling {
if is_comment(s.kind()) {
comments.push(&src[s.byte_range()]);
sibling = s.prev_named_sibling();
} else {
break;
}
}
if comments.is_empty() {
return None;
}
comments.reverse(); // collected nearest-first; TS unshifts to keep source order
Some(
comments
.iter()
.map(|c| clean_comment_markers(c))
.collect::<Vec<_>>()
.join("\n")
.trim()
.to_string(),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strips_line_and_block_markers() {
assert_eq!(clean_comment_markers("// hello"), "hello");
assert_eq!(clean_comment_markers("/// doc line"), "doc line");
assert_eq!(
clean_comment_markers("/**\n * Adds things.\n * @param a first\n */"),
"Adds things.\n@param a first"
);
}
}