@@ -33,22 +33,18 @@ public void execute(Java2GraphConfig config, GraphContext context) throws Except
3333 // 1. ReflectionTypeSolver for classes on the full classpath (not just JRE)
3434 typeSolver .add (new ReflectionTypeSolver (false ));
3535
36- // 2. JavaParserTypeSolver for source code being analyzed
37- typeSolver .add (new JavaParserTypeSolver (config .getSrcDir ()));
38-
36+ // 2. Add Jars BEFORE JavaParserTypeSolver to prevent disk I/O thrashing for library imports
3937 if (config .getJarPaths () != null ) {
4038 for (Path jarPath : config .getJarPaths ()) {
4139 scanAndAddJars (typeSolver , jarPath );
4240 }
4341 }
4442
43+ // 3. We do NOT add JavaParserTypeSolver here. Instead, we use a memory-mapped AstTypeSolver
44+ // built directly from the parsed ASTs to eliminate disk I/O during resolution for large repos.
45+
46+ // Assign the shared TypeSolver for graph context, but DO NOT share the SymbolSolver which is not thread-safe.
4547 context .typeSolver = typeSolver ;
46- JavaSymbolSolver symbolSolver = new JavaSymbolSolver (typeSolver );
47-
48- // Configure JavaParser with the Symbol Solver
49- ParserConfiguration parserConfiguration = new ParserConfiguration ()
50- .setSymbolResolver (symbolSolver )
51- .setLanguageLevel (ParserConfiguration .LanguageLevel .JAVA_17 );
5248
5349 List <Path > javaFiles ;
5450 try (Stream <Path > paths = Files .walk (config .getSrcDir ())) {
@@ -59,8 +55,14 @@ public void execute(Java2GraphConfig config, GraphContext context) throws Except
5955
6056 javaFiles .parallelStream ().forEach (path -> {
6157 try {
62- // Instantiate a new JavaParser per thread
63- JavaParser javaParser = new JavaParser (parserConfiguration );
58+ // JavaSymbolSolver state is mutated during parsing and resolution; MUST be instantiated per-thread
59+ JavaSymbolSolver localSymbolSolver = new JavaSymbolSolver (typeSolver );
60+
61+ ParserConfiguration localConfig = new ParserConfiguration ()
62+ .setSymbolResolver (localSymbolSolver )
63+ .setLanguageLevel (ParserConfiguration .LanguageLevel .JAVA_17 );
64+
65+ JavaParser javaParser = new JavaParser (localConfig );
6466 ParseResult <CompilationUnit > result = javaParser .parse (path );
6567 result .getResult ().ifPresent (cu -> {
6668 context .compilationUnits .put (path .toString (), cu );
@@ -70,7 +72,42 @@ public void execute(Java2GraphConfig config, GraphContext context) throws Except
7072 }
7173 });
7274
73- System .out .println ("Finished parsing." );
75+ System .out .println ("Finished parsing. Building memory-mapped TypeSolver..." );
76+ AstTypeSolver astTypeSolver = new AstTypeSolver ();
77+ for (CompilationUnit cu : context .compilationUnits .values ()) {
78+ astTypeSolver .addCompilationUnit (cu );
79+ }
80+ typeSolver .add (astTypeSolver );
81+ }
82+
83+ private static class AstTypeSolver implements com .github .javaparser .resolution .TypeSolver {
84+ private com .github .javaparser .resolution .TypeSolver parent ;
85+ private final java .util .Map <String , com .github .javaparser .ast .body .TypeDeclaration <?>> types = new java .util .HashMap <>();
86+
87+ public void addCompilationUnit (CompilationUnit cu ) {
88+ for (com .github .javaparser .ast .body .TypeDeclaration <?> t : cu .findAll (com .github .javaparser .ast .body .TypeDeclaration .class )) {
89+ java .util .Optional <String > optFqn = t .getFullyQualifiedName ();
90+ if (optFqn .isPresent ()) {
91+ types .put (optFqn .get (), t );
92+ }
93+ }
94+ }
95+
96+ @ Override
97+ public com .github .javaparser .resolution .TypeSolver getParent () { return parent ; }
98+
99+ @ Override
100+ public void setParent (com .github .javaparser .resolution .TypeSolver parent ) { this .parent = parent ; }
101+
102+ @ Override
103+ public com .github .javaparser .resolution .model .SymbolReference <com .github .javaparser .resolution .declarations .ResolvedReferenceTypeDeclaration > tryToSolveType (String name ) {
104+ com .github .javaparser .ast .body .TypeDeclaration <?> t = types .get (name );
105+ if (t != null ) {
106+ com .github .javaparser .resolution .declarations .ResolvedReferenceTypeDeclaration resolved = com .github .javaparser .symbolsolver .javaparsermodel .JavaParserFacade .get (getRoot ()).getTypeDeclaration (t );
107+ return com .github .javaparser .resolution .model .SymbolReference .solved (resolved );
108+ }
109+ return com .github .javaparser .resolution .model .SymbolReference .unsolved ();
110+ }
74111 }
75112
76113 private void scanAndAddJars (CombinedTypeSolver typeSolver , Path path ) throws IOException {
0 commit comments