Skip to content

Commit cff7f63

Browse files
authored
Merge pull request github#12838 from jcogs33/jcogs33/add-class-for-callables-interesting-for-modeling
Java: add class that represents callables that are interesting for MaD models
2 parents 909f40b + 934a455 commit cff7f63

File tree

10 files changed

+143
-80
lines changed

10 files changed

+143
-80
lines changed

java/ql/lib/semmle/code/java/Member.qll

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,47 @@ class Callable extends StmtParent, Member, @callable {
340340
}
341341
}
342342

343+
/**
344+
* Holds if the given type is public and, if it is a nested type, that all of
345+
* its enclosing types are public as well.
346+
*/
347+
private predicate veryPublic(RefType t) {
348+
t.isPublic() and
349+
(
350+
not t instanceof NestedType or
351+
veryPublic(t.(NestedType).getEnclosingType())
352+
)
353+
}
354+
355+
/** A callable that is the same as its source declaration. */
356+
class SrcCallable extends Callable {
357+
SrcCallable() { this.isSourceDeclaration() }
358+
359+
/**
360+
* Holds if this callable is effectively public in the sense that it can be
361+
* called from outside the codebase. This means either a `public` callable on
362+
* a sufficiently public type or a `protected` callable on a sufficiently
363+
* public non-`final` type.
364+
*/
365+
predicate isEffectivelyPublic() {
366+
exists(RefType t | t = this.getDeclaringType() |
367+
this.isPublic() and veryPublic(t)
368+
or
369+
this.isProtected() and not t.isFinal() and veryPublic(t)
370+
)
371+
or
372+
exists(SrcRefType tsub, Method m |
373+
veryPublic(tsub) and
374+
tsub.hasMethod(m, _) and
375+
m.getSourceDeclaration() = this
376+
|
377+
this.isPublic()
378+
or
379+
this.isProtected() and not tsub.isFinal()
380+
)
381+
}
382+
}
383+
343384
/** Gets the erasure of `t1` if it is a raw type, or `t1` itself otherwise. */
344385
private Type eraseRaw(Type t1) {
345386
if t1 instanceof RawType then result = t1.getErasure() else result = t1
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/** Provides classes and predicates for exclusions related to MaD models. */
2+
3+
import java
4+
5+
/** Holds if the given package `p` is a test package. */
6+
pragma[nomagic]
7+
private predicate isTestPackage(Package p) {
8+
p.getName()
9+
.matches([
10+
"org.junit%", "junit.%", "org.mockito%", "org.assertj%",
11+
"com.github.tomakehurst.wiremock%", "org.hamcrest%", "org.springframework.test.%",
12+
"org.springframework.mock.%", "org.springframework.boot.test.%", "reactor.test%",
13+
"org.xmlunit%", "org.testcontainers.%", "org.opentest4j%", "org.mockserver%",
14+
"org.powermock%", "org.skyscreamer.jsonassert%", "org.rnorth.visibleassertions",
15+
"org.openqa.selenium%", "com.gargoylesoftware.htmlunit%", "org.jboss.arquillian.testng%",
16+
"org.testng%"
17+
])
18+
}
19+
20+
/**
21+
* A test library.
22+
*/
23+
class TestLibrary extends RefType {
24+
TestLibrary() { isTestPackage(this.getPackage()) }
25+
}
26+
27+
/** Holds if the given file is a test file. */
28+
private predicate isInTestFile(File file) {
29+
file.getAbsolutePath().matches(["%/test/%", "%/guava-tests/%", "%/guava-testlib/%"]) and
30+
not file.getAbsolutePath().matches("%/ql/test/%") // allows our test cases to work
31+
}
32+
33+
/** Holds if the given compilation unit's package is a JDK internal. */
34+
private predicate isJdkInternal(CompilationUnit cu) {
35+
cu.getPackage().getName().matches("org.graalvm%") or
36+
cu.getPackage().getName().matches("com.sun%") or
37+
cu.getPackage().getName().matches("sun%") or
38+
cu.getPackage().getName().matches("jdk%") or
39+
cu.getPackage().getName().matches("java2d%") or
40+
cu.getPackage().getName().matches("build.tools%") or
41+
cu.getPackage().getName().matches("propertiesparser%") or
42+
cu.getPackage().getName().matches("org.jcp%") or
43+
cu.getPackage().getName().matches("org.w3c%") or
44+
cu.getPackage().getName().matches("org.ietf.jgss%") or
45+
cu.getPackage().getName().matches("org.xml.sax%") or
46+
cu.getPackage().getName().matches("com.oracle%") or
47+
cu.getPackage().getName().matches("org.omg%") or
48+
cu.getPackage().getName().matches("org.relaxng%") or
49+
cu.getPackage().getName() = "compileproperties" or
50+
cu.getPackage().getName() = "transparentruler" or
51+
cu.getPackage().getName() = "genstubs" or
52+
cu.getPackage().getName() = "netscape.javascript" or
53+
cu.getPackage().getName() = ""
54+
}
55+
56+
/** Holds if the given callable is not worth modeling. */
57+
predicate isUninterestingForModels(Callable c) {
58+
isInTestFile(c.getCompilationUnit().getFile()) or
59+
isJdkInternal(c.getCompilationUnit()) or
60+
c instanceof MainMethod or
61+
c instanceof StaticInitializer or
62+
exists(FunctionalExpr funcExpr | c = funcExpr.asMethod()) or
63+
c.getDeclaringType() instanceof TestLibrary or
64+
c.(Constructor).isParameterless()
65+
}
66+
67+
/**
68+
* A class that represents all callables for which we might be
69+
* interested in having a MaD model.
70+
*/
71+
class ModelApi extends SrcCallable {
72+
ModelApi() {
73+
this.fromSource() and
74+
this.isEffectivelyPublic() and
75+
not isUninterestingForModels(this)
76+
}
77+
}

java/ql/src/Metrics/Summaries/GeneratedVsManualCoverageQuery.qll

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
private import semmle.code.java.dataflow.FlowSummary
2-
private import utils.modelgenerator.internal.CaptureModels
2+
private import semmle.code.java.dataflow.internal.ModelExclusions
33
private import TopJdkApis
44

55
/**
6-
* Returns the number of `DataFlowTargetApi`s with Summary MaD models
6+
* Returns the number of `ModelApi`s with Summary MaD models
77
* for a given package and provenance.
88
*/
99
bindingset[package, apiSubset]
@@ -13,7 +13,7 @@ private int getNumMadModeledApis(string package, string provenance, string apiSu
1313
count(SummarizedCallable sc |
1414
callableSubset(sc.asCallable(), apiSubset) and
1515
package = sc.asCallable().getCompilationUnit().getPackage().getName() and
16-
sc.asCallable() instanceof DataFlowTargetApi and
16+
sc.asCallable() instanceof ModelApi and
1717
(
1818
// "auto-only"
1919
not sc.hasManualModel() and
@@ -34,12 +34,12 @@ private int getNumMadModeledApis(string package, string provenance, string apiSu
3434
)
3535
}
3636

37-
/** Returns the total number of `DataFlowTargetApi`s for a given package. */
37+
/** Returns the total number of `ModelApi`s for a given package. */
3838
private int getNumApis(string package, string apiSubset) {
3939
result =
40-
strictcount(DataFlowTargetApi dataFlowTargApi |
41-
callableSubset(dataFlowTargApi, apiSubset) and
42-
package = dataFlowTargApi.getCompilationUnit().getPackage().getName()
40+
strictcount(ModelApi api |
41+
callableSubset(api, apiSubset) and
42+
package = api.getCompilationUnit().getPackage().getName()
4343
)
4444
}
4545

@@ -70,7 +70,7 @@ predicate modelCoverageGenVsMan(
7070
// calculate the total generated and total manual numbers
7171
generated = generatedOnly + both and
7272
manual = manualOnly + both and
73-
// count the total number of `DataFlowTargetApi`s for each package
73+
// count the total number of `ModelApi`s for each package
7474
all = getNumApis(package, apiSubset) and
7575
non = all - (generatedOnly + both + manualOnly) and
7676
// Proportion of coverage

java/ql/src/Telemetry/ExternalApi.qll

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,7 @@ private import semmle.code.java.dataflow.FlowSummary
88
private import semmle.code.java.dataflow.internal.DataFlowPrivate
99
private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
1010
private import semmle.code.java.dataflow.TaintTracking
11-
12-
pragma[nomagic]
13-
private predicate isTestPackage(Package p) {
14-
p.getName()
15-
.matches([
16-
"org.junit%", "junit.%", "org.mockito%", "org.assertj%",
17-
"com.github.tomakehurst.wiremock%", "org.hamcrest%", "org.springframework.test.%",
18-
"org.springframework.mock.%", "org.springframework.boot.test.%", "reactor.test%",
19-
"org.xmlunit%", "org.testcontainers.%", "org.opentest4j%", "org.mockserver%",
20-
"org.powermock%", "org.skyscreamer.jsonassert%", "org.rnorth.visibleassertions",
21-
"org.openqa.selenium%", "com.gargoylesoftware.htmlunit%", "org.jboss.arquillian.testng%",
22-
"org.testng%"
23-
])
24-
}
25-
26-
/**
27-
* A test library.
28-
*/
29-
private class TestLibrary extends RefType {
30-
TestLibrary() { isTestPackage(this.getPackage()) }
31-
}
11+
private import semmle.code.java.dataflow.internal.ModelExclusions
3212

3313
private string containerAsJar(Container container) {
3414
if container instanceof JarFile then result = container.getBaseName() else result = "rt.jar"

java/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
private import java as J
66
private import semmle.code.java.dataflow.internal.DataFlowPrivate
77
private import semmle.code.java.dataflow.internal.ContainerFlow as ContainerFlow
8+
private import semmle.code.java.dataflow.internal.ModelExclusions
89
private import semmle.code.java.dataflow.DataFlow as Df
910
private import semmle.code.java.dataflow.SSA as Ssa
1011
private import semmle.code.java.dataflow.TaintTracking as Tt
@@ -26,33 +27,6 @@ private J::Method superImpl(J::Method m) {
2627
not m instanceof J::ToStringMethod
2728
}
2829

29-
private predicate isInTestFile(J::File file) {
30-
file.getAbsolutePath().matches(["%/test/%", "%/guava-tests/%", "%/guava-testlib/%"]) and
31-
not file.getAbsolutePath().matches("%/ql/test/%") // allows our test cases to work
32-
}
33-
34-
private predicate isJdkInternal(J::CompilationUnit cu) {
35-
cu.getPackage().getName().matches("org.graalvm%") or
36-
cu.getPackage().getName().matches("com.sun%") or
37-
cu.getPackage().getName().matches("sun%") or
38-
cu.getPackage().getName().matches("jdk%") or
39-
cu.getPackage().getName().matches("java2d%") or
40-
cu.getPackage().getName().matches("build.tools%") or
41-
cu.getPackage().getName().matches("propertiesparser%") or
42-
cu.getPackage().getName().matches("org.jcp%") or
43-
cu.getPackage().getName().matches("org.w3c%") or
44-
cu.getPackage().getName().matches("org.ietf.jgss%") or
45-
cu.getPackage().getName().matches("org.xml.sax%") or
46-
cu.getPackage().getName().matches("com.oracle%") or
47-
cu.getPackage().getName().matches("org.omg%") or
48-
cu.getPackage().getName().matches("org.relaxng%") or
49-
cu.getPackage().getName() = "compileproperties" or
50-
cu.getPackage().getName() = "transparentruler" or
51-
cu.getPackage().getName() = "genstubs" or
52-
cu.getPackage().getName() = "netscape.javascript" or
53-
cu.getPackage().getName() = ""
54-
}
55-
5630
private predicate isInfrequentlyUsed(J::CompilationUnit cu) {
5731
cu.getPackage().getName().matches("javax.swing%") or
5832
cu.getPackage().getName().matches("java.awt%")
@@ -62,13 +36,8 @@ private predicate isInfrequentlyUsed(J::CompilationUnit cu) {
6236
* Holds if it is relevant to generate models for `api`.
6337
*/
6438
private predicate isRelevantForModels(J::Callable api) {
65-
not isInTestFile(api.getCompilationUnit().getFile()) and
66-
not isJdkInternal(api.getCompilationUnit()) and
67-
not isInfrequentlyUsed(api.getCompilationUnit()) and
68-
not api instanceof J::MainMethod and
69-
not api instanceof J::StaticInitializer and
70-
not exists(J::FunctionalExpr funcExpr | api = funcExpr.asMethod()) and
71-
not api.(J::Constructor).isParameterless()
39+
not isUninterestingForModels(api) and
40+
not isInfrequentlyUsed(api.getCompilationUnit())
7241
}
7342

7443
/**

java/ql/test/query-tests/Metrics/GeneratedVsManualCoverage/TopJdkApisTest/TopJdkApis/java/lang/AbstractStringBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence
77
{
88
public char charAt(int p0){ return '0'; } // manual summary
99
public int length(){ return 0; } // manual neutral
10-
public void setCharAt(int p0, char p1){} // manual neutral, Note: not currently counted by query due to exclusions in `TargetApiSpecific`
11-
public void setLength(int p0){} // manual neutral, Note: not currently counted by query due to exclusions in `TargetApiSpecific`
10+
public void setCharAt(int p0, char p1){} // manual neutral
11+
public void setLength(int p0){} // manual neutral
1212

1313

1414
public AbstractStringBuilder append(CharSequence p0){ return null; }

java/ql/test/query-tests/Metrics/GeneratedVsManualCoverage/TopJdkApisTest/TopJdkApis/java/lang/Enum.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
abstract public class Enum<E extends Enum<E>> implements Comparable<E>, Serializable
88
{
99
protected Enum() {}
10-
protected Enum(String p0, int p1){} // manual neutral, Note: this will not be counted in query results since `protected` not `public`
10+
protected Enum(String p0, int p1){} // manual neutral
1111
public String toString(){ return null; } // manual neutral
1212
public final String name(){ return null; } // manual neutral
1313
public final boolean equals(Object p0){ return false; } // manual neutral

java/ql/test/query-tests/Metrics/GeneratedVsManualCoverage/TopJdkApisTest/TopJdkApis/java/lang/StringBuffer.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,4 @@ public class StringBuffer extends AbstractStringBuilder implements Serializable
1212
public StringBuffer append(char p0){ return null; } // manual summary
1313

1414
public StringBuffer append(CharSequence s, int start, int end) { return null; }
15-
16-
public void setCharAt(int p0, char p1){}
17-
public void setLength(int p0){}
1815
}

java/ql/test/query-tests/Metrics/GeneratedVsManualCoverage/TopJdkApisTest/TopJdkApis/java/lang/StringBuilder.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,4 @@ public StringBuilder(String p0){} // manual summary
1919
public StringBuilder(int p0){} // manual summary
2020

2121
public StringBuilder append(CharSequence s, int start, int end) { return null; }
22-
23-
public void setCharAt(int p0, char p1){}
24-
public void setLength(int p0){}
2522
}
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1-
| java.io | 0 | 0 | 22 | 9 | 31 | 0.7096774193548387 | 0.0 | 0.7096774193548387 | 0.0 | NaN | 0.2903225806451613 |
2-
| java.lang | 0 | 0 | 60 | 89 | 149 | 0.40268456375838924 | 0.0 | 0.40268456375838924 | 0.0 | NaN | 0.5973154362416108 |
1+
| java.awt | 0 | 0 | 2 | 1 | 3 | 0.6666666666666666 | 0.0 | 0.6666666666666666 | 0.0 | NaN | 0.3333333333333333 |
2+
| java.io | 0 | 0 | 22 | 15 | 37 | 0.5945945945945946 | 0.0 | 0.5945945945945946 | 0.0 | NaN | 0.40540540540540543 |
3+
| java.lang | 0 | 0 | 62 | 94 | 156 | 0.3974358974358974 | 0.0 | 0.3974358974358974 | 0.0 | NaN | 0.6025641025641025 |
34
| java.lang.invoke | 0 | 0 | 0 | 1 | 1 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
45
| java.lang.reflect | 0 | 0 | 0 | 4 | 4 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
56
| java.math | 0 | 0 | 0 | 16 | 16 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
67
| java.net | 0 | 0 | 5 | 0 | 5 | 1.0 | 0.0 | 1.0 | 0.0 | NaN | 0.0 |
78
| java.nio | 0 | 0 | 2 | 3 | 5 | 0.4 | 0.0 | 0.4 | 0.0 | NaN | 0.6 |
89
| java.nio.charset | 0 | 0 | 0 | 1 | 1 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
9-
| java.nio.file | 0 | 0 | 1 | 1 | 2 | 0.5 | 0.0 | 0.5 | 0.0 | NaN | 0.5 |
10-
| java.sql | 0 | 0 | 0 | 2 | 2 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
10+
| java.nio.file | 0 | 0 | 7 | 1 | 8 | 0.875 | 0.0 | 0.875 | 0.0 | NaN | 0.125 |
11+
| java.sql | 0 | 0 | 2 | 14 | 16 | 0.125 | 0.0 | 0.125 | 0.0 | NaN | 0.875 |
1112
| java.text | 0 | 0 | 0 | 5 | 5 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
1213
| java.time | 0 | 0 | 0 | 17 | 17 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
1314
| java.time.chrono | 0 | 0 | 0 | 1 | 1 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
1415
| java.time.format | 0 | 0 | 0 | 2 | 2 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
15-
| java.util | 0 | 0 | 56 | 45 | 101 | 0.5544554455445545 | 0.0 | 0.5544554455445545 | 0.0 | NaN | 0.44554455445544555 |
16-
| java.util.concurrent | 0 | 0 | 6 | 7 | 13 | 0.46153846153846156 | 0.0 | 0.46153846153846156 | 0.0 | NaN | 0.5384615384615384 |
16+
| java.util | 0 | 0 | 84 | 68 | 152 | 0.5526315789473685 | 0.0 | 0.5526315789473685 | 0.0 | NaN | 0.4473684210526316 |
17+
| java.util.concurrent | 0 | 0 | 9 | 9 | 18 | 0.5 | 0.0 | 0.5 | 0.0 | NaN | 0.5 |
1718
| java.util.concurrent.atomic | 0 | 0 | 2 | 11 | 13 | 0.15384615384615385 | 0.0 | 0.15384615384615385 | 0.0 | NaN | 0.8461538461538461 |
18-
| java.util.function | 0 | 0 | 0 | 1 | 1 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
19+
| java.util.concurrent.locks | 0 | 0 | 0 | 2 | 2 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
20+
| java.util.function | 0 | 0 | 0 | 6 | 6 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
1921
| java.util.logging | 0 | 0 | 1 | 1 | 2 | 0.5 | 0.0 | 0.5 | 0.0 | NaN | 0.5 |
2022
| java.util.regex | 0 | 0 | 3 | 1 | 4 | 0.75 | 0.0 | 0.75 | 0.0 | NaN | 0.25 |
21-
| java.util.stream | 0 | 0 | 4 | 5 | 9 | 0.4444444444444444 | 0.0 | 0.4444444444444444 | 0.0 | NaN | 0.5555555555555556 |
23+
| java.util.stream | 0 | 0 | 18 | 8 | 26 | 0.6923076923076923 | 0.0 | 0.6923076923076923 | 0.0 | NaN | 0.3076923076923077 |

0 commit comments

Comments
 (0)