Skip to content

Commit dd0a511

Browse files
committed
Fix issue javaparser#3071 - Hanging when certain names are resolved
1 parent da28937 commit dd0a511

File tree

2 files changed

+310
-5
lines changed
  • javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts
  • javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver

2 files changed

+310
-5
lines changed

javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/StatementContext.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ protected Optional<Value> solveWithAsValue(SymbolDeclarator symbolDeclarator, St
189189

190190
@Override
191191
public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name) {
192+
return solveSymbol(name, true);
193+
}
194+
195+
/**
196+
* Used where a symbol is being used (e.g. solving {@code x} when used as an argument {@code doubleThis(x)}, or calculation {@code return x * 2;}).
197+
* @param name the variable / reference / identifier used.
198+
* @param iterateAdjacentStmts flag to iterate adjacent statements, default to {@code true} except in cases to prevent repetitive checks.
199+
* @return // FIXME: Better documentation on how this is different to solveSymbolAsValue()
200+
*/
201+
private SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, boolean iterateAdjacentStmts) {
192202

193203
/*
194204
* If we're in a variable declaration line.
@@ -201,7 +211,7 @@ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String na
201211
return symbolReference;
202212
}
203213

204-
/*
214+
/*
205215
* If we're in a statement that contains a pattern expression.
206216
* Example: {@code double x = a instanceof String s;}
207217
*/
@@ -228,6 +238,13 @@ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String na
228238
} else if (parentOfWrappedNode instanceof LambdaExpr) {
229239
return solveSymbolInParentContext(name);
230240
} else if (parentOfWrappedNode instanceof NodeWithStatements) {
241+
// If we choose to not solve adjacent statements instead attempt to solve via the parent.
242+
// Further below is an explanation for why we may want to disable this visitation of adjacent statements
243+
// to prevent revisiting the same contexts over and over again.
244+
if (!iterateAdjacentStmts) {
245+
return solveSymbolInParentContext(name);
246+
}
247+
231248
NodeWithStatements<?> nodeWithStmt = (NodeWithStatements<?>) parentOfWrappedNode;
232249

233250
// Assuming the wrapped node exists within the parent's collection of statements...
@@ -239,14 +256,18 @@ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String na
239256
// Start at the current node and work backwards...
240257
ListIterator<Statement> statementListIterator = nodeWithStmt.getStatements().listIterator(position);
241258
while(statementListIterator.hasPrevious()) {
242-
symbolReference = JavaParserFactory.getContext(statementListIterator.previous(), typeSolver).solveSymbol(name);
259+
Context prevContext = JavaParserFactory.getContext(statementListIterator.previous(), typeSolver);
260+
if (prevContext instanceof StatementContext) {
261+
// Do not schedule each adjacent statement to do this exact same thing.
262+
// Since we are doing it here we will eventually cover all adjacent statements anyways.
263+
symbolReference = ((StatementContext<?>)prevContext).solveSymbol(name, false);
264+
} else {
265+
symbolReference = prevContext.solveSymbol(name);
266+
}
243267
if (symbolReference.isSolved()) {
244268
return symbolReference;
245269
}
246270
}
247-
248-
// If nothing is found, attempt to solve within the parent context
249-
return solveSymbolInParentContext(name);
250271
}
251272

252273
// If nothing is found, attempt to solve within the parent context
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
package com.github.javaparser.symbolsolver;
2+
3+
import com.github.javaparser.ParserConfiguration;
4+
import com.github.javaparser.StaticJavaParser;
5+
import com.github.javaparser.ast.CompilationUnit;
6+
import com.github.javaparser.ast.expr.NameExpr;
7+
import com.github.javaparser.ast.nodeTypes.NodeWithStatements;
8+
import com.github.javaparser.resolution.UnsolvedSymbolException;
9+
import com.github.javaparser.symbolsolver.resolution.AbstractResolutionTest;
10+
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.jupiter.api.Timeout;
13+
14+
import java.util.List;
15+
import java.util.concurrent.TimeUnit;
16+
17+
/**
18+
* An issue when resolving some name when there are a series of many prior {@link NodeWithStatements}s.
19+
* Each queues up solving in the prior adjacent statement, which means we queue up duplicate resolve calls.
20+
* <br>
21+
* Naturally this will result in an exponential growth in number of calls with the more prior statements.
22+
* If not fixed the example test cases below take hours to complete
23+
* <i>(They were tested and ran up to 75 minutes before manually being stopped)</i>.
24+
*/
25+
public class Issue3038Test extends AbstractResolutionTest {
26+
// In no way should this take more than 2.5 seconds
27+
// Realistically this should take much less.
28+
private static final long TIME_LIMIT_MS = 2500;
29+
30+
@Test
31+
@Timeout(value = TIME_LIMIT_MS, unit = TimeUnit.MILLISECONDS)
32+
public void test3038() {
33+
String code = "public class Foo{\n" +
34+
" public static void main(String[] args) {\n" +
35+
" String s0 = \"hello\";\n" +
36+
" String s1 = \"hello\";\n" +
37+
" String s2 = \"hello\";\n" +
38+
" String s3 = \"hello\";\n" +
39+
" String s4 = \"hello\";\n" +
40+
" String s5 = \"hello\";\n" +
41+
" String s6 = \"hello\";\n" +
42+
" String s7 = \"hello\";\n" +
43+
" String s8 = \"hello\";\n" +
44+
" String s9 = \"hello\";\n" +
45+
" String s10 = \"hello\";\n" +
46+
" String s11 = \"hello\";\n" +
47+
" String s12 = \"hello\";\n" +
48+
" String s13 = \"hello\";\n" +
49+
" String s14 = \"hello\";\n" +
50+
" String s15 = \"hello\";\n" +
51+
" String s16 = \"hello\";\n" +
52+
" String s17 = \"hello\";\n" +
53+
" String s18 = \"hello\";\n" +
54+
" String s19 = \"hello\";\n" +
55+
" String s20 = \"hello\";\n" +
56+
" String s21 = \"hello\";\n" +
57+
" String s22 = \"hello\";\n" +
58+
" String s23 = \"hello\";\n" +
59+
" String s24 = \"hello\";\n" +
60+
" String s25 = \"hello\";\n" +
61+
" String s26 = \"hello\";\n" +
62+
" String s27 = \"hello\";\n" +
63+
" String s28 = \"hello\";\n" +
64+
" String s29 = \"hello\";\n" +
65+
" String s30 = \"hello\";\n" +
66+
" String s31 = \"hello\";\n" +
67+
" String s32 = \"hello\";\n" +
68+
" String s33 = \"hello\";\n" +
69+
" String s34 = \"hello\";\n" +
70+
" String s35 = \"hello\";\n" +
71+
" String s36 = \"hello\";\n" +
72+
" String s37 = \"hello\";\n" +
73+
" String s38 = \"hello\";\n" +
74+
" String s39 = \"hello\";\n" +
75+
" String s40 = \"hello\";\n" +
76+
" String s41 = \"hello\";\n" +
77+
" String s42 = \"hello\";\n" +
78+
" String s43 = \"hello\";\n" +
79+
" String s44 = \"hello\";\n" +
80+
" String s45 = \"hello\";\n" +
81+
" String s46 = \"hello\";\n" +
82+
" String s47 = \"hello\";\n" +
83+
" String s48 = \"hello\";\n" +
84+
" String s49 = \"hello\";\n" +
85+
" String s50 = \"hello\";\n" +
86+
" String s51 = \"hello\";\n" +
87+
" String s52 = \"hello\";\n" +
88+
" String s53 = \"hello\";\n" +
89+
" String s54 = \"hello\";\n" +
90+
" String s55 = \"hello\";\n" +
91+
" String s56 = \"hello\";\n" +
92+
" String s57 = \"hello\";\n" +
93+
" String s58 = \"hello\";\n" +
94+
" String s59 = \"hello\";\n" +
95+
" String s60 = \"hello\";\n" +
96+
" String s61 = \"hello\";\n" +
97+
" String s62 = \"hello\";\n" +
98+
" String s63 = \"hello\";\n" +
99+
" String s64 = \"hello\";\n" +
100+
" String s65 = \"hello\";\n" +
101+
" String s66 = \"hello\";\n" +
102+
" String s67 = \"hello\";\n" +
103+
" String s68 = \"hello\";\n" +
104+
" String s69 = \"hello\";\n" +
105+
" String s70 = \"hello\";\n" +
106+
" String s71 = \"hello\";\n" +
107+
" String s72 = \"hello\";\n" +
108+
" String s73 = \"hello\";\n" +
109+
" String s74 = \"hello\";\n" +
110+
" String s75 = \"hello\";\n" +
111+
" String s76 = \"hello\";\n" +
112+
" String s77 = \"hello\";\n" +
113+
" String s78 = \"hello\";\n" +
114+
" String s79 = \"hello\";\n" +
115+
" String s80 = \"hello\";\n" +
116+
" String s81 = \"hello\";\n" +
117+
" String s82 = \"hello\";\n" +
118+
" String s83 = \"hello\";\n" +
119+
" String s84 = \"hello\";\n" +
120+
" String s85 = \"hello\";\n" +
121+
" String s86 = \"hello\";\n" +
122+
" String s87 = \"hello\";\n" +
123+
" String s88 = \"hello\";\n" +
124+
" String s89 = \"hello\";\n" +
125+
" String s90 = \"hello\";\n" +
126+
" String s91 = \"hello\";\n" +
127+
" String s92 = \"hello\";\n" +
128+
" String s93 = \"hello\";\n" +
129+
" String s94 = \"hello\";\n" +
130+
" String s95 = \"hello\";\n" +
131+
" String s96 = \"hello\";\n" +
132+
" String s97 = \"hello\";\n" +
133+
" String s98 = \"hello\";\n" +
134+
" String s99 = \"hello\";\n" +
135+
" String s100 = \"hello\";\n" +
136+
" new Thread(){\n" +
137+
" @Override\n" +
138+
" public void run() {\n" +
139+
" Foo foo = Foo.getInstance();\n" +
140+
" }\n" +
141+
" }.run();\n" +
142+
" }\n" +
143+
" static Foo getInstance() {\n" +
144+
" return new Foo();\n" +
145+
" }\n" +
146+
"}";
147+
run(code);
148+
}
149+
150+
@Test
151+
@Timeout(value = TIME_LIMIT_MS, unit = TimeUnit.MILLISECONDS)
152+
public void testAlt3038() {
153+
String code = "public class Foo{\n" +
154+
" public static void main(String[] args) {\n" +
155+
" String s0 = \"hello\";\n" +
156+
" String s1 = \"hello\";\n" +
157+
" String s2 = \"hello\";\n" +
158+
" String s3 = \"hello\";\n" +
159+
" String s4 = \"hello\";\n" +
160+
" String s5 = \"hello\";\n" +
161+
" String s6 = \"hello\";\n" +
162+
" String s7 = \"hello\";\n" +
163+
" String s8 = \"hello\";\n" +
164+
" String s9 = \"hello\";\n" +
165+
" String s10 = \"hello\";\n" +
166+
" String s11 = \"hello\";\n" +
167+
" String s12 = \"hello\";\n" +
168+
" String s13 = \"hello\";\n" +
169+
" String s14 = \"hello\";\n" +
170+
" String s15 = \"hello\";\n" +
171+
" String s16 = \"hello\";\n" +
172+
" String s17 = \"hello\";\n" +
173+
" String s18 = \"hello\";\n" +
174+
" String s19 = \"hello\";\n" +
175+
" String s20 = \"hello\";\n" +
176+
" String s21 = \"hello\";\n" +
177+
" String s22 = \"hello\";\n" +
178+
" String s23 = \"hello\";\n" +
179+
" String s24 = \"hello\";\n" +
180+
" String s25 = \"hello\";\n" +
181+
" String s26 = \"hello\";\n" +
182+
" String s27 = \"hello\";\n" +
183+
" String s28 = \"hello\";\n" +
184+
" String s29 = \"hello\";\n" +
185+
" String s30 = \"hello\";\n" +
186+
" String s31 = \"hello\";\n" +
187+
" String s32 = \"hello\";\n" +
188+
" String s33 = \"hello\";\n" +
189+
" String s34 = \"hello\";\n" +
190+
" String s35 = \"hello\";\n" +
191+
" String s36 = \"hello\";\n" +
192+
" String s37 = \"hello\";\n" +
193+
" String s38 = \"hello\";\n" +
194+
" String s39 = \"hello\";\n" +
195+
" String s40 = \"hello\";\n" +
196+
" String s41 = \"hello\";\n" +
197+
" String s42 = \"hello\";\n" +
198+
" String s43 = \"hello\";\n" +
199+
" String s44 = \"hello\";\n" +
200+
" String s45 = \"hello\";\n" +
201+
" String s46 = \"hello\";\n" +
202+
" String s47 = \"hello\";\n" +
203+
" String s48 = \"hello\";\n" +
204+
" String s49 = \"hello\";\n" +
205+
" String s50 = \"hello\";\n" +
206+
" String s51 = \"hello\";\n" +
207+
" String s52 = \"hello\";\n" +
208+
" String s53 = \"hello\";\n" +
209+
" String s54 = \"hello\";\n" +
210+
" String s55 = \"hello\";\n" +
211+
" String s56 = \"hello\";\n" +
212+
" String s57 = \"hello\";\n" +
213+
" String s58 = \"hello\";\n" +
214+
" String s59 = \"hello\";\n" +
215+
" String s60 = \"hello\";\n" +
216+
" String s61 = \"hello\";\n" +
217+
" String s62 = \"hello\";\n" +
218+
" String s63 = \"hello\";\n" +
219+
" String s64 = \"hello\";\n" +
220+
" String s65 = \"hello\";\n" +
221+
" String s66 = \"hello\";\n" +
222+
" String s67 = \"hello\";\n" +
223+
" String s68 = \"hello\";\n" +
224+
" String s69 = \"hello\";\n" +
225+
" String s70 = \"hello\";\n" +
226+
" String s71 = \"hello\";\n" +
227+
" String s72 = \"hello\";\n" +
228+
" String s73 = \"hello\";\n" +
229+
" String s74 = \"hello\";\n" +
230+
" String s75 = \"hello\";\n" +
231+
" String s76 = \"hello\";\n" +
232+
" String s77 = \"hello\";\n" +
233+
" String s78 = \"hello\";\n" +
234+
" String s79 = \"hello\";\n" +
235+
" String s80 = \"hello\";\n" +
236+
" String s81 = \"hello\";\n" +
237+
" String s82 = \"hello\";\n" +
238+
" String s83 = \"hello\";\n" +
239+
" String s84 = \"hello\";\n" +
240+
" String s85 = \"hello\";\n" +
241+
" String s86 = \"hello\";\n" +
242+
" String s87 = \"hello\";\n" +
243+
" String s88 = \"hello\";\n" +
244+
" String s89 = \"hello\";\n" +
245+
" String s90 = \"hello\";\n" +
246+
" String s91 = \"hello\";\n" +
247+
" String s92 = \"hello\";\n" +
248+
" String s93 = \"hello\";\n" +
249+
" String s94 = \"hello\";\n" +
250+
" String s95 = \"hello\";\n" +
251+
" String s96 = \"hello\";\n" +
252+
" String s97 = \"hello\";\n" +
253+
" String s98 = \"hello\";\n" +
254+
" String s99 = \"hello\";\n" +
255+
" String s100 = \"hello\";\n" +
256+
" Foo foo = Foo.getInstance();\n" +
257+
" }\n" +
258+
" static Foo getInstance() {\n" +
259+
" return new Foo();\n" +
260+
" }\n" +
261+
"}";
262+
run(code);
263+
}
264+
265+
private void run(String code) {
266+
ParserConfiguration config = new ParserConfiguration();
267+
config.setSymbolResolver(new JavaSymbolSolver(new ReflectionTypeSolver(false)));
268+
StaticJavaParser.setConfiguration(config);
269+
270+
CompilationUnit cu = StaticJavaParser.parse(code);
271+
272+
List<NameExpr> exprs = cu.findAll(NameExpr.class);
273+
for (NameExpr expr : exprs) {
274+
long start = System.currentTimeMillis();
275+
try {
276+
expr.resolve();
277+
} catch (UnsolvedSymbolException ex) {
278+
// this is expected since we have no way for the resolver to find "Foo"
279+
}
280+
long end = System.currentTimeMillis();
281+
System.out.printf("Call to resolve '%s' took %dms", expr.toString(), (end - start));
282+
}
283+
}
284+
}

0 commit comments

Comments
 (0)