mirror of
https://github.com/github/codeql.git
synced 2025-12-20 18:56:32 +01:00
Java: Initial implementation of type based model generation.
This commit is contained in:
@@ -67,6 +67,8 @@ private predicate isRelevantForModels(J::Callable api) {
|
||||
*/
|
||||
predicate isRelevantForDataFlowModels = isRelevantForModels/1;
|
||||
|
||||
predicate isRelevantForTypeBasedFlowModels = isRelevantForModels/1;
|
||||
|
||||
/**
|
||||
* A class of Callables that are relevant for generating summary, source and sinks models for.
|
||||
*
|
||||
@@ -175,7 +177,7 @@ predicate isRelevantType(J::Type t) {
|
||||
*/
|
||||
string qualifierString() { result = "Argument[-1]" }
|
||||
|
||||
private string parameterAccess(J::Parameter p) {
|
||||
string parameterAccess(J::Parameter p) {
|
||||
if
|
||||
p.getType() instanceof J::Array and
|
||||
not isPrimitiveTypeUsedForBulkData(p.getType().(J::Array).getElementType())
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
private import java
|
||||
private import semmle.code.java.Collections
|
||||
private import CaptureModelsSpecific as Specific
|
||||
private import CaptureModels
|
||||
|
||||
/**
|
||||
* TODO: We might just inline this instead.
|
||||
* Holds if `t` is a collection of type `tv` (eg. `List<T>`)
|
||||
*/
|
||||
private predicate genericCollectionType(CollectionType t, TypeVariable tv) {
|
||||
t.getElementType() = tv
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tv` is a type variable of the immediate type declaring `callable`.
|
||||
*/
|
||||
private predicate classTypeParameter(Callable callable, TypeVariable tv) {
|
||||
callable.getDeclaringType().(GenericType).getATypeParameter() = tv
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tv` is type variable of `callable` or the type declaring `callable`.
|
||||
*/
|
||||
private predicate localTypeParameter(Callable callable, TypeVariable tv) {
|
||||
classTypeParameter(callable, tv) or
|
||||
callable.(GenericCallable).getATypeParameter() = tv
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `callable` has a type parameter `tv`
|
||||
* or collection parameterized over type `tv`.
|
||||
*/
|
||||
private predicate parameter(Callable callable, string input, TypeVariable tv) {
|
||||
exists(Parameter p |
|
||||
input = Specific::parameterAccess(p) and
|
||||
p = callable.getAParameter() and
|
||||
(
|
||||
// Parameter of type tv
|
||||
p.getType() = tv
|
||||
or
|
||||
// Parameter is a collection of type tv
|
||||
genericCollectionType(p.getType(), tv)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of a synthetic field corresponding to `tv`.
|
||||
*/
|
||||
private string getSyntheticField(TypeVariable tv) {
|
||||
result = ".SyntheticField[ArgType" + tv.getIndex() + "]"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a models as data string representation of, how a value of type `tv`
|
||||
* can be read or stored implicitly in relation to `callable`.
|
||||
*/
|
||||
private string implicit(Callable callable, TypeVariable tv) {
|
||||
classTypeParameter(callable, tv) and
|
||||
exists(string access |
|
||||
if genericCollectionType(callable.getDeclaringType(), tv)
|
||||
then access = ".Element"
|
||||
else access = getSyntheticField(tv)
|
||||
|
|
||||
result = Specific::qualifierString() + access
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A class of types that represents functions.
|
||||
*/
|
||||
private class Function extends ParameterizedType {
|
||||
Function() {
|
||||
exists(FunctionalInterface fi |
|
||||
fi = this.getGenericType() and
|
||||
fi.hasName("Function")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter type of `this` function.
|
||||
*/
|
||||
Type getParameterType() { result = this.getTypeArgument(0) }
|
||||
|
||||
/**
|
||||
* Get the return type of `this` function.
|
||||
*/
|
||||
Type getReturnType() { result = this.getTypeArgument(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `callable` has a functional interface parameter `fi` at parameter position `position`.
|
||||
*/
|
||||
private predicate functional(Callable callable, Function f, int position) {
|
||||
exists(Parameter p |
|
||||
p = callable.getAParameter() and
|
||||
f = p.getType() and
|
||||
position = p.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets models as data input/output access relative to the type parameter `tv` in the
|
||||
* type `t` in the scope of `callable`.
|
||||
*
|
||||
* Note: This predicate has to be inlined as `callable` is not related to `return` or `tv`
|
||||
* in every disjunction.
|
||||
*/
|
||||
bindingset[callable]
|
||||
private string getAccess(Callable callable, Type return, TypeVariable tv) {
|
||||
return = tv and result = ""
|
||||
or
|
||||
genericCollectionType(return, tv) and result = ".Element"
|
||||
or
|
||||
not genericCollectionType(return, tv) and
|
||||
(
|
||||
return.(ParameterizedType).getATypeArgument() = tv
|
||||
or
|
||||
callable.getDeclaringType() = return and return.(GenericType).getATypeParameter() = tv
|
||||
) and
|
||||
result = getSyntheticField(tv)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `input` is a models as data string representation of, how a value of type `tv`
|
||||
* (or a generic parameterized over `tv`) can be generated by a functionalinterface parameter of `callable`.
|
||||
*/
|
||||
private predicate functionalSource(Callable callable, string input, TypeVariable tv) {
|
||||
exists(Function f, int position, Type return, string access |
|
||||
functional(callable, f, position) and
|
||||
return = f.getReturnType() and
|
||||
access = getAccess(callable, return, tv) and
|
||||
input = "Argument[" + position + "].ReturnValue" + access
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `input` is a models as data string representation of, how a
|
||||
* value of type `tv` (or a generic parameterized over `tv`)
|
||||
* can be provided as input to `callable`.
|
||||
* This includes
|
||||
* (1) The implicit synthetic field(s) of the declaring type of `callable`.
|
||||
* (2) The parameters of `callable`.
|
||||
* (3) Any delegate parameters of `callable`.
|
||||
*/
|
||||
private predicate input(Callable callable, string input, TypeVariable tv) {
|
||||
input = implicit(callable, tv)
|
||||
or
|
||||
parameter(callable, input, tv)
|
||||
or
|
||||
functionalSource(callable, input, tv)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `callable` returns a value of type `tv` (or a generic parameterized over `tv`) and `output`
|
||||
* is a models as data string representation of, how data is routed to the return.
|
||||
*/
|
||||
private predicate returns(Callable callable, TypeVariable tv, string output) {
|
||||
exists(Type return, string access | return = callable.getReturnType() |
|
||||
access = getAccess(callable, return, tv) and
|
||||
output = "ReturnValue" + access
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `callable` has a functional interface parameter that accepts a value of type `tv`
|
||||
* and `output` is the models as data string representation of, how data is routed to
|
||||
* the delegate parameter.
|
||||
*/
|
||||
private predicate functionalSink(Callable callable, TypeVariable tv, string output) {
|
||||
exists(Function f, int position |
|
||||
functional(callable, f, position) and
|
||||
tv = f.getParameterType() and
|
||||
output = "Argument[" + position + "]" + ".Parameter[0]"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `output` is a models as data string representation of, how values of type `tv`
|
||||
* (or generics parameterized over `tv`) can be routed.
|
||||
* This includes
|
||||
* (1) The implicit synthetic field(s) of the declaring type of `callable`.
|
||||
* (2) The return of `callable`.
|
||||
* (3) Any delegate parameters of `callable`.
|
||||
*/
|
||||
private predicate output(Callable callable, TypeVariable tv, string output) {
|
||||
output = implicit(callable, tv)
|
||||
or
|
||||
returns(callable, tv, output)
|
||||
or
|
||||
functionalSink(callable, tv, output)
|
||||
}
|
||||
|
||||
/**
|
||||
* A class of callables that are relevant generating summaries for based
|
||||
* on the Theorems for Free approach.
|
||||
*/
|
||||
class TypeBasedFlowTargetApi extends Specific::TargetApiSpecific {
|
||||
TypeBasedFlowTargetApi() { Specific::isRelevantForTypeBasedFlowModels(this) }
|
||||
|
||||
/**
|
||||
* Gets the string representation of all type based summaries for `this`
|
||||
* inspired by the Theorems for Free approach.
|
||||
*
|
||||
* Examples could be (see C# pseudo code below)
|
||||
* (1) `Get` returns a value of type `T`. We assume that the returned
|
||||
* value was fetched from a (synthetic) field.
|
||||
* (2) `Set` consumes a value of type `T`. We assume that the value is stored in
|
||||
* a (synthetic) field.
|
||||
* (3) `Apply<S>` is assumed to apply the provided function to a value stored in
|
||||
* a (synthetic) field and return the result.
|
||||
* (4) `Apply<S1,S2>` is assumed to apply the provided function to provided value
|
||||
* and return the result.
|
||||
* ```csharp
|
||||
* public class MyGeneric<T> {
|
||||
* public void Set(T x) { ... }
|
||||
* public T Get() { ... }
|
||||
* public S Apply<S>(Func<T, S> f) { ... }
|
||||
* public S2 Apply<S1, S2>(S1 x, Func<S1, S2> f) { ... }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
string getSummaries() {
|
||||
exists(TypeVariable tv, string input, string output |
|
||||
localTypeParameter(this, tv) and
|
||||
input(this, input, tv) and
|
||||
output(this, tv, output) and
|
||||
input != output
|
||||
|
|
||||
result = asValueModel(this, input, output)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Theorems for Free inspired typed based summaries for `api`.
|
||||
*/
|
||||
string captureFlow(TypeBasedFlowTargetApi api) { result = api.getSummaries() }
|
||||
Reference in New Issue
Block a user