22
33import com .github .javaparser .JavaParser ;
44import com .github .javaparser .ParserConfiguration ;
5+ import com .github .javaparser .ParseResult ;
56import com .github .javaparser .ast .CompilationUnit ;
7+ import com .github .javaparser .ast .body .TypeDeclaration ;
68import com .github .javaparser .symbolsolver .JavaSymbolSolver ;
79import com .github .javaparser .symbolsolver .resolution .typesolvers .CombinedTypeSolver ;
810import com .github .javaparser .symbolsolver .resolution .typesolvers .JarTypeSolver ;
9- import com .github .javaparser .symbolsolver .resolution .typesolvers .JavaParserTypeSolver ;
1011import com .github .javaparser .symbolsolver .resolution .typesolvers .ReflectionTypeSolver ;
12+ import com .github .javaparser .symbolsolver .resolution .typesolvers .ClassLoaderTypeSolver ;
1113import com .neuvem .java2graph .Java2GraphConfig ;
1214import com .neuvem .java2graph .models .GraphContext ;
13- import com .github .javaparser .ParseResult ;
1415
1516import java .io .IOException ;
17+ import java .nio .MappedByteBuffer ;
18+ import java .nio .channels .FileChannel ;
19+ import java .nio .charset .StandardCharsets ;
1620import java .nio .file .Files ;
1721import java .nio .file .Path ;
22+ import java .nio .file .StandardOpenOption ;
1823import java .util .List ;
24+ import java .util .Map ;
25+ import java .util .Optional ;
26+ import java .util .concurrent .ConcurrentHashMap ;
1927import java .util .stream .Collectors ;
2028import java .util .stream .Stream ;
2129
@@ -26,51 +34,75 @@ public class ParsePass implements Pass {
2634
2735 @ Override
2836 public void execute (Java2GraphConfig config , GraphContext context ) throws Exception {
29- System .out .println ("Configuring Symbol Solver..." );
30-
31- CombinedTypeSolver typeSolver = new CombinedTypeSolver ();
32-
33- // 1. ReflectionTypeSolver for classes on the full classpath (not just JRE)
34- typeSolver .add (new ReflectionTypeSolver (false ));
37+ System .out .println ("Beginning Two-Pass Parsing..." );
3538
36- // 2. JavaParserTypeSolver for source code being analyzed
37- typeSolver .add (new JavaParserTypeSolver (config .getSrcDir ()));
38-
39- if (config .getJarPaths () != null ) {
40- for (Path jarPath : config .getJarPaths ()) {
41- scanAndAddJars (typeSolver , jarPath );
42- }
43- }
44-
45- 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 );
52-
5339 List <Path > javaFiles ;
5440 try (Stream <Path > paths = Files .walk (config .getSrcDir ())) {
5541 javaFiles = paths .filter (p -> p .toString ().endsWith (".java" )).collect (Collectors .toList ());
5642 }
5743
58- System .out .println ("Parsing " + javaFiles .size () + " files..." );
44+ // Pass 1: Parse all files WITHOUT the symbol solver to build the index
45+ // This avoids "cold start" resolution failures during parallel parsing.
46+ System .out .println ("Pass 1: Parsing " + javaFiles .size () + " files..." );
47+
48+ ParserConfiguration initialConfig = new ParserConfiguration ()
49+ .setLanguageLevel (ParserConfiguration .LanguageLevel .JAVA_17 )
50+ .setStoreTokens (true ) // Restore token storage for AST completeness
51+ .setAttributeComments (true );
52+
53+ Map <String , CompilationUnit > cuIndex = new ConcurrentHashMap <>();
5954
60- javaFiles .parallelStream ().forEach (path -> {
55+ javaFiles .parallelStream ().forEach (( Path path ) -> {
6156 try {
62- // Instantiate a new JavaParser per thread
63- JavaParser javaParser = new JavaParser ( parserConfiguration );
57+ com . github . javaparser . JavaParser javaParser = new com . github . javaparser . JavaParser ( initialConfig );
58+ // Standard parser call (handles encoding and storage metadata)
6459 ParseResult <CompilationUnit > result = javaParser .parse (path );
6560 result .getResult ().ifPresent (cu -> {
6661 context .compilationUnits .put (path .toString (), cu );
62+
63+ cu .findAll (TypeDeclaration .class ).forEach (td -> {
64+ Optional <String > fqnOpt = ((TypeDeclaration <?>)td ).getFullyQualifiedName ();
65+ if (fqnOpt .isPresent ()) {
66+ cuIndex .put (fqnOpt .get (), cu );
67+ }
68+ });
6769 });
6870 } catch (Throwable e ) {
6971 System .err .println ("Failed to parse: " + path + " - " + e .getMessage ());
7072 }
7173 });
74+
75+ System .out .println ("Pass 2: Configuring Symbol Resolver with " + cuIndex .size () + " indexed types..." );
76+
77+ CombinedTypeSolver typeSolver = new CombinedTypeSolver ();
78+
79+ // 1. SourceMemoryTypeSolver MUST BE FIRST to avoid shadowing by JARs or JRE
80+ typeSolver .add (new SourceMemoryTypeSolver (cuIndex ));
81+
82+ // 2. ReflectionTypeSolver for standard JRE classes
83+ typeSolver .add (new ReflectionTypeSolver (true ));
7284
73- System .out .println ("Finished parsing." );
85+ // 3. ClassLoaderTypeSolver for the current thread classloader
86+ typeSolver .add (new ClassLoaderTypeSolver (Thread .currentThread ().getContextClassLoader ()));
87+
88+ // 4. Fallback reflection (includes app classpath)
89+ typeSolver .add (new ReflectionTypeSolver (false ));
90+
91+ if (config .getJarPaths () != null ) {
92+ for (Path jarPath : config .getJarPaths ()) {
93+ scanAndAddJars (typeSolver , jarPath );
94+ }
95+ }
96+
97+ context .typeSolver = typeSolver ;
98+ JavaSymbolSolver symbolSolver = new JavaSymbolSolver (typeSolver );
99+
100+ // Attach the resolver to all CompilationUnits for the resolution pass
101+ context .compilationUnits .values ().forEach (cu -> {
102+ cu .setData (com .github .javaparser .ast .Node .SYMBOL_RESOLVER_KEY , symbolSolver );
103+ });
104+
105+ System .out .println ("Finished two-pass parsing and resolver configuration." );
74106 }
75107
76108 private void scanAndAddJars (CombinedTypeSolver typeSolver , Path path ) throws IOException {
0 commit comments