Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bdc569f

Browse files
committed
Java: Initial implementation of type based model generation.
1 parent eea062d commit bdc569f

3 files changed

Lines changed: 244 additions & 1 deletion

File tree

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ class TypeVariable extends BoundedType, Modifiable, @typevariable {
195195
result = this.getASuppliedType().(TypeVariable).getAnUltimatelySuppliedType()
196196
}
197197

198+
/** Gets the index of `this` type variable. */
199+
int getIndex() { typeVars(this, _, result, _, _) }
200+
198201
override string getAPrimaryQlClass() { result = "TypeVariable" }
199202
}
200203

java/ql/src/utils/model-generator/internal/CaptureModelsSpecific.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ private predicate isRelevantForModels(J::Callable api) {
6767
*/
6868
predicate isRelevantForDataFlowModels = isRelevantForModels/1;
6969

70+
predicate isRelevantForTypeBasedFlowModels = isRelevantForModels/1;
71+
7072
/**
7173
* A class of Callables that are relevant for generating summary, source and sinks models for.
7274
*
@@ -175,7 +177,7 @@ predicate isRelevantType(J::Type t) {
175177
*/
176178
string qualifierString() { result = "Argument[-1]" }
177179

178-
private string parameterAccess(J::Parameter p) {
180+
string parameterAccess(J::Parameter p) {
179181
if
180182
p.getType() instanceof J::Array and
181183
not isPrimitiveTypeUsedForBulkData(p.getType().(J::Array).getElementType())
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
private import java
2+
private import semmle.code.java.Collections
3+
private import CaptureModelsSpecific as Specific
4+
private import CaptureModels
5+
6+
/**
7+
* TODO: We might just inline this instead.
8+
* Holds if `t` is a collection of type `tv` (eg. `List<T>`)
9+
*/
10+
private predicate genericCollectionType(CollectionType t, TypeVariable tv) {
11+
t.getElementType() = tv
12+
}
13+
14+
/**
15+
* Holds if `tv` is a type variable of the immediate type declaring `callable`.
16+
*/
17+
private predicate classTypeParameter(Callable callable, TypeVariable tv) {
18+
callable.getDeclaringType().(GenericType).getATypeParameter() = tv
19+
}
20+
21+
/**
22+
* Holds if `tv` is type variable of `callable` or the type declaring `callable`.
23+
*/
24+
private predicate localTypeParameter(Callable callable, TypeVariable tv) {
25+
classTypeParameter(callable, tv) or
26+
callable.(GenericCallable).getATypeParameter() = tv
27+
}
28+
29+
/**
30+
* Holds if `callable` has a type parameter `tv`
31+
* or collection parameterized over type `tv`.
32+
*/
33+
private predicate parameter(Callable callable, string input, TypeVariable tv) {
34+
exists(Parameter p |
35+
input = Specific::parameterAccess(p) and
36+
p = callable.getAParameter() and
37+
(
38+
// Parameter of type tv
39+
p.getType() = tv
40+
or
41+
// Parameter is a collection of type tv
42+
genericCollectionType(p.getType(), tv)
43+
)
44+
)
45+
}
46+
47+
/**
48+
* Gets the string representation of a synthetic field corresponding to `tv`.
49+
*/
50+
private string getSyntheticField(TypeVariable tv) {
51+
result = ".SyntheticField[ArgType" + tv.getIndex() + "]"
52+
}
53+
54+
/**
55+
* Gets a models as data string representation of, how a value of type `tv`
56+
* can be read or stored implicitly in relation to `callable`.
57+
*/
58+
private string implicit(Callable callable, TypeVariable tv) {
59+
classTypeParameter(callable, tv) and
60+
exists(string access |
61+
if genericCollectionType(callable.getDeclaringType(), tv)
62+
then access = ".Element"
63+
else access = getSyntheticField(tv)
64+
|
65+
result = Specific::qualifierString() + access
66+
)
67+
}
68+
69+
/**
70+
* A class of types that represents functions.
71+
*/
72+
private class Function extends ParameterizedType {
73+
Function() {
74+
exists(FunctionalInterface fi |
75+
fi = this.getGenericType() and
76+
fi.hasName("Function")
77+
)
78+
}
79+
80+
/**
81+
* Gets the parameter type of `this` function.
82+
*/
83+
Type getParameterType() { result = this.getTypeArgument(0) }
84+
85+
/**
86+
* Get the return type of `this` function.
87+
*/
88+
Type getReturnType() { result = this.getTypeArgument(1) }
89+
}
90+
91+
/**
92+
* Holds if `callable` has a functional interface parameter `fi` at parameter position `position`.
93+
*/
94+
private predicate functional(Callable callable, Function f, int position) {
95+
exists(Parameter p |
96+
p = callable.getAParameter() and
97+
f = p.getType() and
98+
position = p.getPosition()
99+
)
100+
}
101+
102+
/**
103+
* Gets models as data input/output access relative to the type parameter `tv` in the
104+
* type `t` in the scope of `callable`.
105+
*
106+
* Note: This predicate has to be inlined as `callable` is not related to `return` or `tv`
107+
* in every disjunction.
108+
*/
109+
bindingset[callable]
110+
private string getAccess(Callable callable, Type return, TypeVariable tv) {
111+
return = tv and result = ""
112+
or
113+
genericCollectionType(return, tv) and result = ".Element"
114+
or
115+
not genericCollectionType(return, tv) and
116+
(
117+
return.(ParameterizedType).getATypeArgument() = tv
118+
or
119+
callable.getDeclaringType() = return and return.(GenericType).getATypeParameter() = tv
120+
) and
121+
result = getSyntheticField(tv)
122+
}
123+
124+
/**
125+
* Holds if `input` is a models as data string representation of, how a value of type `tv`
126+
* (or a generic parameterized over `tv`) can be generated by a functionalinterface parameter of `callable`.
127+
*/
128+
private predicate functionalSource(Callable callable, string input, TypeVariable tv) {
129+
exists(Function f, int position, Type return, string access |
130+
functional(callable, f, position) and
131+
return = f.getReturnType() and
132+
access = getAccess(callable, return, tv) and
133+
input = "Argument[" + position + "].ReturnValue" + access
134+
)
135+
}
136+
137+
/**
138+
* Holds if `input` is a models as data string representation of, how a
139+
* value of type `tv` (or a generic parameterized over `tv`)
140+
* can be provided as input to `callable`.
141+
* This includes
142+
* (1) The implicit synthetic field(s) of the declaring type of `callable`.
143+
* (2) The parameters of `callable`.
144+
* (3) Any delegate parameters of `callable`.
145+
*/
146+
private predicate input(Callable callable, string input, TypeVariable tv) {
147+
input = implicit(callable, tv)
148+
or
149+
parameter(callable, input, tv)
150+
or
151+
functionalSource(callable, input, tv)
152+
}
153+
154+
/**
155+
* Holds if `callable` returns a value of type `tv` (or a generic parameterized over `tv`) and `output`
156+
* is a models as data string representation of, how data is routed to the return.
157+
*/
158+
private predicate returns(Callable callable, TypeVariable tv, string output) {
159+
exists(Type return, string access | return = callable.getReturnType() |
160+
access = getAccess(callable, return, tv) and
161+
output = "ReturnValue" + access
162+
)
163+
}
164+
165+
/**
166+
* Holds if `callable` has a functional interface parameter that accepts a value of type `tv`
167+
* and `output` is the models as data string representation of, how data is routed to
168+
* the delegate parameter.
169+
*/
170+
private predicate functionalSink(Callable callable, TypeVariable tv, string output) {
171+
exists(Function f, int position |
172+
functional(callable, f, position) and
173+
tv = f.getParameterType() and
174+
output = "Argument[" + position + "]" + ".Parameter[0]"
175+
)
176+
}
177+
178+
/**
179+
* Holds if `output` is a models as data string representation of, how values of type `tv`
180+
* (or generics parameterized over `tv`) can be routed.
181+
* This includes
182+
* (1) The implicit synthetic field(s) of the declaring type of `callable`.
183+
* (2) The return of `callable`.
184+
* (3) Any delegate parameters of `callable`.
185+
*/
186+
private predicate output(Callable callable, TypeVariable tv, string output) {
187+
output = implicit(callable, tv)
188+
or
189+
returns(callable, tv, output)
190+
or
191+
functionalSink(callable, tv, output)
192+
}
193+
194+
/**
195+
* A class of callables that are relevant generating summaries for based
196+
* on the Theorems for Free approach.
197+
*/
198+
class TypeBasedFlowTargetApi extends Specific::TargetApiSpecific {
199+
TypeBasedFlowTargetApi() { Specific::isRelevantForTypeBasedFlowModels(this) }
200+
201+
/**
202+
* Gets the string representation of all type based summaries for `this`
203+
* inspired by the Theorems for Free approach.
204+
*
205+
* Examples could be (see C# pseudo code below)
206+
* (1) `Get` returns a value of type `T`. We assume that the returned
207+
* value was fetched from a (synthetic) field.
208+
* (2) `Set` consumes a value of type `T`. We assume that the value is stored in
209+
* a (synthetic) field.
210+
* (3) `Apply<S>` is assumed to apply the provided function to a value stored in
211+
* a (synthetic) field and return the result.
212+
* (4) `Apply<S1,S2>` is assumed to apply the provided function to provided value
213+
* and return the result.
214+
* ```csharp
215+
* public class MyGeneric<T> {
216+
* public void Set(T x) { ... }
217+
* public T Get() { ... }
218+
* public S Apply<S>(Func<T, S> f) { ... }
219+
* public S2 Apply<S1, S2>(S1 x, Func<S1, S2> f) { ... }
220+
* }
221+
* ```
222+
*/
223+
string getSummaries() {
224+
exists(TypeVariable tv, string input, string output |
225+
localTypeParameter(this, tv) and
226+
input(this, input, tv) and
227+
output(this, tv, output) and
228+
input != output
229+
|
230+
result = asValueModel(this, input, output)
231+
)
232+
}
233+
}
234+
235+
/**
236+
* Returns the Theorems for Free inspired typed based summaries for `api`.
237+
*/
238+
string captureFlow(TypeBasedFlowTargetApi api) { result = api.getSummaries() }

0 commit comments

Comments
 (0)