|
| 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