Skip to content

Commit 0b6c5f1

Browse files
SONARJAVA-5571 Change sonar-java-symbolic-execution-plugin to not embed the java-frontend dependency but use the provided one (#5164)
1 parent b0637a3 commit 0b6c5f1

16 files changed

Lines changed: 538 additions & 43 deletions

File tree

java-symbolic-execution/java-symbolic-execution-plugin/pom.xml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
<configuration>
5050
<rules>
5151
<requireFilesSize>
52-
<maxsize>15000000</maxsize>
53-
<minsize>10000000</minsize>
52+
<maxsize>1500000</maxsize>
53+
<minsize>900000</minsize>
5454
<files>
5555
<file>${project.build.directory}/${project.build.finalName}.jar</file>
5656
</files>
@@ -86,12 +86,6 @@
8686
<exclude>NOTICE*</exclude>
8787
</excludes>
8888
</filter>
89-
<filter>
90-
<artifact>org.sonarsource.java:java-frontend</artifact>
91-
<includes>
92-
<include>**</include>
93-
</includes>
94-
</filter>
9589
</filters>
9690
</configuration>
9791
</execution>
@@ -131,6 +125,7 @@
131125
<groupId>${project.groupId}</groupId>
132126
<artifactId>java-frontend</artifactId>
133127
<version>${project.version}</version>
128+
<scope>provided</scope>
134129
</dependency>
135130
<dependency>
136131
<groupId>org.sonarsource.api.plugin</groupId>
@@ -160,6 +155,10 @@
160155
<groupId>org.sonarsource.analyzer-commons</groupId>
161156
<artifactId>sonar-analyzer-commons</artifactId>
162157
</dependency>
158+
<dependency>
159+
<groupId>org.apache.commons</groupId>
160+
<artifactId>commons-lang3</artifactId>
161+
</dependency>
163162
<dependency>
164163
<groupId>org.sonarsource.java</groupId>
165164
<artifactId>test-classpath-reader</artifactId>

java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/cfg/SELiveVariables.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ private static Set<Symbol> getUsedVariables(@Nullable Tree syntaxNode, Symbol.Me
163163
if (syntaxNode == null) {
164164
return Collections.emptySet();
165165
}
166-
VariableReadExtractor extractorFromClass = new VariableReadExtractor(owner, false);
166+
SEVariableReadExtractor extractorFromClass = new SEVariableReadExtractor(owner, false);
167167
syntaxNode.accept(extractorFromClass);
168168
return extractorFromClass.usedVariables();
169169
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* SonarQube Java
3+
* Copyright (C) 2012-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonar.java.cfg;
18+
19+
import java.util.HashSet;
20+
import java.util.Set;
21+
import org.sonar.plugins.java.api.semantic.Symbol;
22+
import org.sonar.plugins.java.api.semantic.Symbol.MethodSymbol;
23+
import org.sonar.plugins.java.api.tree.AssignmentExpressionTree;
24+
import org.sonar.plugins.java.api.tree.BaseTreeVisitor;
25+
import org.sonar.plugins.java.api.tree.ClassTree;
26+
import org.sonar.plugins.java.api.tree.IdentifierTree;
27+
import org.sonar.plugins.java.api.tree.LambdaExpressionTree;
28+
import org.sonar.plugins.java.api.tree.Tree.Kind;
29+
import org.sonar.plugins.java.api.tree.VariableTree;
30+
31+
public class SEVariableReadExtractor extends BaseTreeVisitor {
32+
33+
private final MethodSymbol methodSymbol;
34+
private final Set<Symbol> used;
35+
private final boolean includeFields;
36+
37+
public SEVariableReadExtractor(MethodSymbol methodSymbol, boolean includeFields) {
38+
this.methodSymbol = methodSymbol;
39+
this.includeFields = includeFields;
40+
used = new HashSet<>();
41+
}
42+
43+
public Set<Symbol> usedVariables() {
44+
return used;
45+
}
46+
47+
@Override
48+
public void visitAssignmentExpression(AssignmentExpressionTree tree) {
49+
//skip writing to a variable or field.
50+
if(!tree.variable().is(Kind.IDENTIFIER) && !tree.variable().is(Kind.MEMBER_SELECT)) {
51+
scan(tree.variable());
52+
}
53+
scan(tree.expression());
54+
}
55+
56+
@Override
57+
public void visitVariable(VariableTree tree) {
58+
// skip variable modifiers and simple name
59+
scan(tree.initializer());
60+
}
61+
62+
@Override
63+
public void visitClass(ClassTree tree) {
64+
// skip modifiers, parameters, simple name and superclass/interface
65+
scan(tree.members());
66+
}
67+
68+
@Override
69+
public void visitLambdaExpression(LambdaExpressionTree lambdaExpressionTree) {
70+
// skip variable declaration
71+
scan(lambdaExpressionTree.body());
72+
}
73+
74+
@Override
75+
public void visitIdentifier(IdentifierTree tree) {
76+
Symbol owner = tree.symbol().owner();
77+
if(methodSymbol.equals(owner) || (includeFields && isField(tree.symbol(), methodSymbol.owner()))) {
78+
used.add(tree.symbol());
79+
}
80+
super.visitIdentifier(tree);
81+
}
82+
83+
private static boolean isField(Symbol identifierSymbol, Symbol methodOwnerSymbol) {
84+
return methodOwnerSymbol.equals(identifierSymbol.owner()) && !"this".equals(identifierSymbol.name()) && !identifierSymbol.isMethodSymbol();
85+
}
86+
87+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* SonarQube Java
3+
* Copyright (C) 2012-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonar.java.matcher;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.regex.Matcher;
22+
import java.util.regex.Pattern;
23+
import org.sonar.plugins.java.api.semantic.MethodMatchers;
24+
25+
public class SEMethodMatcherFactory {
26+
27+
private static final Pattern CLASS_PATTERN = Pattern.compile("^(\\w+[\\.\\w+]*(?:\\[\\])?)(\\()?");
28+
private static final Pattern METHOD_PATTERN = Pattern.compile("^([a-zA-Z_0-9$]+(?:\\.[a-zA-Z_0-9$]+)*+(?:\\[])?)#(\\w+)(\\()?");
29+
private static final Pattern ARGUMENT_PATTERN = Pattern.compile("\\G(\\w+(?:\\.\\w+)*+(?:\\[])?)([,)])");
30+
31+
private SEMethodMatcherFactory() {
32+
// no instances, only static, factory methods
33+
}
34+
35+
public static MethodMatchers constructorMatcher(String descriptor) {
36+
Matcher matcher = CLASS_PATTERN.matcher(descriptor);
37+
if (!matcher.find()) {
38+
throw new IllegalArgumentException("Illegal constructor specification: " + descriptor);
39+
}
40+
MethodMatchers.ParametersBuilder constructorMatcher = MethodMatchers.create().ofTypes(matcher.group(1)).constructor();
41+
return collectArguments(descriptor, matcher, 2, constructorMatcher);
42+
}
43+
44+
public static MethodMatchers methodMatchers(String descriptor) {
45+
Matcher matcher = METHOD_PATTERN.matcher(descriptor);
46+
if (!matcher.find()) {
47+
throw new IllegalArgumentException("Illegal method specification: " + descriptor);
48+
}
49+
MethodMatchers.ParametersBuilder methodMatcher = MethodMatchers.create().ofTypes(matcher.group(1)).names(matcher.group(2));
50+
return collectArguments(descriptor, matcher, 3, methodMatcher);
51+
}
52+
53+
public static MethodMatchers collectArguments(String descriptor, Matcher initialMatcher, int groupOffset, MethodMatchers.ParametersBuilder methodMatcher) {
54+
if ("(".equals(initialMatcher.group(groupOffset))) {
55+
String remainder = descriptor.substring(initialMatcher.group().length());
56+
if (!")".equals(remainder)) {
57+
Matcher matcher = ARGUMENT_PATTERN.matcher(remainder);
58+
int matchedLength = 0;
59+
List<String> argumentTypes = new ArrayList<>();
60+
while (matcher.find()) {
61+
argumentTypes.add(matcher.group(1));
62+
matchedLength = matcher.end();
63+
}
64+
if (matchedLength < remainder.length()) {
65+
throw new IllegalArgumentException("Illegal method or constructor arguments specification: " + descriptor);
66+
}
67+
return methodMatcher.addParametersMatcher(argumentTypes.toArray(new String[0])).build();
68+
} else {
69+
return methodMatcher.addWithoutParametersMatcher().build();
70+
}
71+
} else {
72+
if (initialMatcher.end() < descriptor.length()) {
73+
throw new IllegalArgumentException("Illegal method or constructor arguments specification: " + descriptor);
74+
}
75+
return methodMatcher.withAnyParameters().build();
76+
}
77+
}
78+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* SonarQube Java
3+
* Copyright (C) 2012-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
@ParametersAreNonnullByDefault
18+
@MethodsAreNonnullByDefault
19+
package org.sonar.java.matcher;
20+
21+
import javax.annotation.ParametersAreNonnullByDefault;
22+
import org.sonar.plugins.java.api.tree.MethodsAreNonnullByDefault;

java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/ExplodedGraphWalker.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import javax.annotation.CheckForNull;
3434
import javax.annotation.Nullable;
3535
import org.sonar.java.Preconditions;
36-
import org.sonar.java.annotations.VisibleForTesting;
3736
import org.sonar.java.cfg.SELiveVariables;
3837
import org.sonar.java.model.CFGUtils;
3938
import org.sonar.java.model.SEExpressionUtils;
@@ -108,7 +107,7 @@ public class ExplodedGraphWalker {
108107
private static final int MAX_STARTING_STATES = 1_024;
109108
private static final Set<String> THIS_SUPER = SetUtils.immutableSetOf("this", "super");
110109

111-
@VisibleForTesting
110+
// VisibleForTesting
112111
static final int MAX_EXEC_PROGRAM_POINT = 2;
113112

114113
private static final MethodMatchers SYSTEM_EXIT_MATCHER = MethodMatchers.create().ofTypes("java.lang.System").names("exit").addParametersMatcher("int").build();
@@ -133,19 +132,19 @@ public class ExplodedGraphWalker {
133132

134133
private ExplodedGraph explodedGraph;
135134

136-
@VisibleForTesting
135+
// VisibleForTesting
137136
Deque<ExplodedGraph.Node> workList;
138137
ExplodedGraph.Node node;
139138
ProgramPoint programPosition;
140139
ProgramState programState;
141140
private SELiveVariables liveVariables;
142-
@VisibleForTesting
141+
// VisibleForTesting
143142
CheckerDispatcher checkerDispatcher;
144143
private Block exitBlock;
145144

146145
private final Sema semanticModel;
147146
private final BehaviorCache behaviorCache;
148-
@VisibleForTesting
147+
// VisibleForTesting
149148
int steps;
150149

151150
ConstraintManager constraintManager;
@@ -181,7 +180,7 @@ public MaximumStartingStatesException(String s) {
181180
}
182181
}
183182

184-
@VisibleForTesting
183+
// VisibleForTesting
185184
public ExplodedGraphWalker(BehaviorCache behaviorCache, JavaFileScannerContext context) {
186185
List<SECheck> checks = Arrays.asList(new NullDereferenceCheck(), new DivisionByZeroCheck(),
187186
new UnclosedResourcesCheck(), new LocksNotUnlockedCheck(), new NonNullSetToNullCheck(), new NoWayOutLoopCheck());
@@ -191,13 +190,13 @@ public ExplodedGraphWalker(BehaviorCache behaviorCache, JavaFileScannerContext c
191190
this.semanticModel = (Sema) context.getSemanticModel();
192191
}
193192

194-
@VisibleForTesting
193+
// VisibleForTesting
195194
ExplodedGraphWalker(BehaviorCache behaviorCache, JavaFileScannerContext context, boolean cleanup) {
196195
this(behaviorCache, context);
197196
this.cleanup = cleanup;
198197
}
199198

200-
@VisibleForTesting
199+
// VisibleForTesting
201200
protected ExplodedGraphWalker(List<SECheck> seChecks, BehaviorCache behaviorCache, JavaFileScannerContext context) {
202201
this.alwaysTrueOrFalseExpressionCollector = new AlwaysTrueOrFalseExpressionCollector();
203202
this.checkerDispatcher = new CheckerDispatcher(this, seChecks, context);
@@ -1293,7 +1292,7 @@ private void checkExplodedGraphTooBig(ProgramState programState) {
12931292
}
12941293
}
12951294

1296-
@VisibleForTesting
1295+
// VisibleForTesting
12971296
protected int maxSteps() {
12981297
return MAX_STEPS;
12991298
}
@@ -1309,7 +1308,7 @@ AlwaysTrueOrFalseExpressionCollector alwaysTrueOrFalseExpressionCollector() {
13091308
*/
13101309
public static class ExplodedGraphWalkerFactory {
13111310

1312-
@VisibleForTesting
1311+
// VisibleForTesting
13131312
final List<SECheck> seChecks = new ArrayList<>();
13141313

13151314
public ExplodedGraphWalkerFactory(List<SECheck> activeSEChecks) {

java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/ProgramState.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import javax.annotation.CheckForNull;
2929
import javax.annotation.Nullable;
3030
import org.sonar.java.Preconditions;
31-
import org.sonar.java.annotations.VisibleForTesting;
3231
import org.sonar.java.se.checks.CustomUnclosedResourcesCheck;
3332
import org.sonar.java.se.checks.LocksNotUnlockedCheck;
3433
import org.sonar.java.se.checks.StreamConsumedCheck;
@@ -347,7 +346,7 @@ public ProgramState removeConstraintsOnDomain(SymbolicValue sv, Class<? extends
347346
* To be used only by the ExplodedGraphWalker only, when manipulating program states.
348347
* Only made 'public' because of some method yield tests.
349348
*/
350-
@VisibleForTesting
349+
// VisibleForTesting
351350
public ProgramState put(Symbol symbol, SymbolicValue value) {
352351
if (symbol.isUnknown() || isVolatileField(symbol)) {
353352
return this;

java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/SymbolicExecutionVisitor.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.util.List;
2222
import org.slf4j.Logger;
2323
import org.slf4j.LoggerFactory;
24-
import org.sonar.java.annotations.VisibleForTesting;
2524
import org.sonar.java.se.checks.SECheck;
2625
import org.sonar.java.se.xproc.BehaviorCache;
2726
import org.sonar.java.se.xproc.MethodBehavior;
@@ -36,7 +35,7 @@ public class SymbolicExecutionVisitor extends BaseTreeVisitor implements JavaFil
3635
private static final Logger LOG = LoggerFactory.getLogger(SymbolicExecutionVisitor.class);
3736
protected JavaFileScannerContext context;
3837

39-
@VisibleForTesting
38+
// VisibleForTesting
4039
public final BehaviorCache behaviorCache;
4140
private final ExplodedGraphWalker.ExplodedGraphWalkerFactory egwFactory;
4241

@@ -86,7 +85,7 @@ public void execute(MethodTree methodTree) {
8685
}
8786
}
8887

89-
@VisibleForTesting
88+
// VisibleForTesting
9089
protected ExplodedGraphWalker getWalker() {
9190
return egwFactory.createWalker(behaviorCache, context);
9291
}

java-symbolic-execution/java-symbolic-execution-plugin/src/main/java/org/sonar/java/se/checks/CustomUnclosedResourcesCheck.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import javax.annotation.Nullable;
2222
import org.sonar.check.Rule;
2323
import org.sonar.check.RuleProperty;
24-
import org.sonar.java.matcher.MethodMatcherFactory;
24+
import org.sonar.java.matcher.SEMethodMatcherFactory;
2525
import org.sonar.java.se.CheckerContext;
2626
import org.sonar.java.se.ExplodedGraph;
2727
import org.sonar.java.se.Flow;
@@ -139,7 +139,7 @@ private static String name(Tree tree) {
139139

140140
private static MethodMatchers createMethodMatchers(String rule) {
141141
if (rule.length() > 0) {
142-
return MethodMatcherFactory.methodMatchers(rule);
142+
return SEMethodMatcherFactory.methodMatchers(rule);
143143
} else {
144144
return MethodMatchers.none();
145145
}
@@ -248,7 +248,7 @@ private MethodMatchers constructorClasses() {
248248
if (classConstructor == null) {
249249
classConstructor = MethodMatchers.none();
250250
if (constructor.length() > 0) {
251-
classConstructor = MethodMatcherFactory.constructorMatcher(constructor);
251+
classConstructor = SEMethodMatcherFactory.constructorMatcher(constructor);
252252
}
253253
}
254254
return classConstructor;

0 commit comments

Comments
 (0)