Java: Deduplicate unification code as a parameterised module.

This commit is contained in:
Anders Schack-Mulligen
2022-08-18 10:42:36 +02:00
parent 4df2e5d937
commit 6b01f02df6
4 changed files with 107 additions and 245 deletions

View File

@@ -4,6 +4,7 @@ private import DataFlowUtil
private import semmle.code.java.dataflow.InstanceAccess
private import semmle.code.java.dataflow.FlowSummary
private import semmle.code.java.dispatch.VirtualDispatch as VirtualDispatch
private import semmle.code.java.dispatch.Unification
private module DispatchImpl {
/** Gets a viable implementation of the target of the given `Call`. */
@@ -115,74 +116,20 @@ private module DispatchImpl {
exact = false and
exists(RefType t2 |
result.asCallable() = VirtualDispatch::viableMethodImpl(def, t.getSourceDeclaration(), t2) and
not failsUnification(t, t2)
not Unification::failsUnification(t, t2)
)
or
result.asSummarizedCallable() = def
)
}
pragma[noinline]
private predicate unificationTargetLeft(ParameterizedType t1, GenericType g) {
contextArgHasType(_, _, t1, _) and t1.getGenericType() = g
private predicate unificationTargetLeft(ParameterizedType t1) { contextArgHasType(_, _, t1, _) }
private predicate unificationTargetRight(ParameterizedType t2) {
exists(VirtualDispatch::viableMethodImpl(_, _, t2))
}
pragma[noinline]
private predicate unificationTargetRight(ParameterizedType t2, GenericType g) {
exists(VirtualDispatch::viableMethodImpl(_, _, t2)) and t2.getGenericType() = g
}
private predicate unificationTargets(Type t1, Type t2) {
exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g))
or
exists(Array a1, Array a2 |
unificationTargets(a1, a2) and
t1 = a1.getComponentType() and
t2 = a2.getComponentType()
)
or
exists(ParameterizedType pt1, ParameterizedType pt2, int pos |
unificationTargets(pt1, pt2) and
not pt1.getSourceDeclaration() != pt2.getSourceDeclaration() and
t1 = pt1.getTypeArgument(pos) and
t2 = pt2.getTypeArgument(pos)
)
}
pragma[noinline]
private predicate typeArgsOfUnificationTargets(
ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2
) {
unificationTargets(t1, t2) and
arg1 = t1.getTypeArgument(pos) and
arg2 = t2.getTypeArgument(pos)
}
private predicate failsUnification(Type t1, Type t2) {
unificationTargets(t1, t2) and
(
exists(RefType arg1, RefType arg2 |
typeArgsOfUnificationTargets(t1, t2, _, arg1, arg2) and
failsUnification(arg1, arg2)
)
or
failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType())
or
not (
t1 instanceof Array and t2 instanceof Array
or
t1.(PrimitiveType) = t2.(PrimitiveType)
or
t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration()
or
t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration()
or
t1 instanceof BoundedType and t2 instanceof RefType
or
t1 instanceof RefType and t2 instanceof BoundedType
)
)
}
private module Unification = MkUnification<unificationTargetLeft/1, unificationTargetRight/1>;
private int parameterPosition() { result in [-1, any(Parameter p).getPosition()] }

View File

@@ -15,6 +15,7 @@ private import semmle.code.java.dataflow.internal.DataFlowUtil
private import semmle.code.java.dataflow.internal.DataFlowPrivate
private import semmle.code.java.dataflow.internal.ContainerFlow
private import semmle.code.java.dataflow.InstanceAccess
private import semmle.code.java.dispatch.Unification
/**
* Gets a viable dispatch target for `ma`. This is the input dispatch relation.
@@ -266,67 +267,10 @@ Method viableImpl_out(MethodAccess ma) {
)
}
private module Unification {
pragma[noinline]
private predicate unificationTargetLeft(ParameterizedType t1, GenericType g) {
objectToStringQualType(_, t1) and t1.getGenericType() = g
}
private predicate unificationTargetLeft(ParameterizedType t1) { objectToStringQualType(_, t1) }
pragma[noinline]
private predicate unificationTargetRight(ParameterizedType t2, GenericType g) {
exists(viableMethodImpl(_, _, t2)) and t2.getGenericType() = g
}
private predicate unificationTargets(Type t1, Type t2) {
exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g))
or
exists(Array a1, Array a2 |
unificationTargets(a1, a2) and
t1 = a1.getComponentType() and
t2 = a2.getComponentType()
)
or
exists(ParameterizedType pt1, ParameterizedType pt2, int pos |
unificationTargets(pt1, pt2) and
not pt1.getSourceDeclaration() != pt2.getSourceDeclaration() and
t1 = pt1.getTypeArgument(pos) and
t2 = pt2.getTypeArgument(pos)
)
}
pragma[noinline]
private predicate typeArgsOfUnificationTargets(
ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2
) {
unificationTargets(t1, t2) and
arg1 = t1.getTypeArgument(pos) and
arg2 = t2.getTypeArgument(pos)
}
pragma[nomagic]
predicate failsUnification(Type t1, Type t2) {
unificationTargets(t1, t2) and
(
exists(RefType arg1, RefType arg2 |
typeArgsOfUnificationTargets(t1, t2, _, arg1, arg2) and
failsUnification(arg1, arg2)
)
or
failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType())
or
not (
t1 instanceof Array and t2 instanceof Array
or
t1.(PrimitiveType) = t2.(PrimitiveType)
or
t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration()
or
t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration()
or
t1 instanceof BoundedType and t2 instanceof RefType
or
t1 instanceof RefType and t2 instanceof BoundedType
)
)
}
private predicate unificationTargetRight(ParameterizedType t2) {
exists(viableMethodImpl(_, _, t2))
}
private module Unification = MkUnification<unificationTargetLeft/1, unificationTargetRight/1>;

View File

@@ -0,0 +1,84 @@
/**
* Provides a module to check whether two `ParameterizedType`s are unifiable.
*/
import java
/** Holds if `t` is a relevant type to consider for unification. */
signature predicate unificationTarget(ParameterizedType t);
/**
* Given two sets of parameterised types to consider for unification, returns
* the set of pairs that are not unifiable.
*/
module MkUnification<unificationTarget/1 targetLeft, unificationTarget/1 targetRight> {
pragma[noinline]
private predicate unificationTargetLeft(ParameterizedType t1, GenericType g) {
targetLeft(t1) and t1.getGenericType() = g
}
pragma[noinline]
private predicate unificationTargetRight(ParameterizedType t2, GenericType g) {
targetRight(t2) and t2.getGenericType() = g
}
private predicate unificationTargets(Type t1, Type t2) {
exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g))
or
exists(Array a1, Array a2 |
unificationTargets(a1, a2) and
t1 = a1.getComponentType() and
t2 = a2.getComponentType()
)
or
exists(ParameterizedType pt1, ParameterizedType pt2, int pos |
unificationTargets(pt1, pt2) and
not pt1.getSourceDeclaration() != pt2.getSourceDeclaration() and
t1 = pt1.getTypeArgument(pos) and
t2 = pt2.getTypeArgument(pos)
)
}
pragma[noinline]
private predicate typeArgsOfUnificationTargets(
ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2
) {
unificationTargets(t1, t2) and
arg1 = t1.getTypeArgument(pos) and
arg2 = t2.getTypeArgument(pos)
}
/**
* Holds if `t1` and `t2` are not unifiable.
*
* Restricted to only consider pairs of types such that `targetLeft(t1)`,
* `targetRight(t2)`, and that both are parameterised instances of the same
* generic type.
*/
pragma[nomagic]
predicate failsUnification(Type t1, Type t2) {
unificationTargets(t1, t2) and
(
exists(RefType arg1, RefType arg2 |
typeArgsOfUnificationTargets(t1, t2, _, arg1, arg2) and
failsUnification(arg1, arg2)
)
or
failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType())
or
not (
t1 instanceof Array and t2 instanceof Array
or
t1.(PrimitiveType) = t2.(PrimitiveType)
or
t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration()
or
t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration()
or
t1 instanceof BoundedType and t2 instanceof RefType
or
t1 instanceof RefType and t2 instanceof BoundedType
)
)
}
}

View File

@@ -9,6 +9,7 @@ private import DispatchFlow as DispatchFlow
private import ObjFlow as ObjFlow
private import semmle.code.java.dataflow.internal.BaseSSA
private import semmle.code.java.controlflow.Guards
private import semmle.code.java.dispatch.Unification
/**
* A conservative analysis that returns a single method - if we can establish
@@ -91,69 +92,10 @@ private module Dispatch {
)
}
private module Unification_v2 {
pragma[noinline]
private predicate unificationTargetLeft(ParameterizedType t1, GenericType g) {
qualType(_, t1, _) and t1.getGenericType() = g
}
private predicate unificationTargetLeft_v2(ParameterizedType t1) { qualType(_, t1, _) }
pragma[noinline]
private predicate unificationTargetRight(ParameterizedType t2, GenericType g) {
exists(viableMethodImpl(_, _, t2)) and t2.getGenericType() = g
}
private predicate unificationTargets(Type t1, Type t2) {
exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g))
or
exists(Array a1, Array a2 |
unificationTargets(a1, a2) and
t1 = a1.getComponentType() and
t2 = a2.getComponentType()
)
or
exists(ParameterizedType pt1, ParameterizedType pt2, int pos |
unificationTargets(pt1, pt2) and
not pt1.getSourceDeclaration() != pt2.getSourceDeclaration() and
t1 = pt1.getTypeArgument(pos) and
t2 = pt2.getTypeArgument(pos)
)
}
pragma[noinline]
private predicate typeArgsOfUnificationTargets(
ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2
) {
unificationTargets(t1, t2) and
arg1 = t1.getTypeArgument(pos) and
arg2 = t2.getTypeArgument(pos)
}
predicate failsUnification(Type t1, Type t2) {
unificationTargets(t1, t2) and
(
exists(RefType arg1, RefType arg2 |
typeArgsOfUnificationTargets(t1, t2, _, arg1, arg2) and
failsUnification(arg1, arg2)
)
or
failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType())
or
not (
t1 instanceof Array and t2 instanceof Array
or
t1.(PrimitiveType) = t2.(PrimitiveType)
or
t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration()
or
t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration()
or
t1 instanceof BoundedType and t2 instanceof RefType
or
t1 instanceof RefType and t2 instanceof BoundedType
)
)
}
}
private module Unification_v2 =
MkUnification<unificationTargetLeft_v2/1, unificationTargetRight/1>;
/**
* INTERNAL: Use `viableImpl` instead.
@@ -203,70 +145,15 @@ private module Dispatch {
else result = source.getMethod().getSourceDeclaration()
}
private module Unification_v1 {
pragma[noinline]
private predicate unificationTargetLeft(ParameterizedType t1, GenericType g) {
hasQualifierType(_, t1, _) and t1.getGenericType() = g
}
private predicate unificationTargetLeft_v1(ParameterizedType t1) { hasQualifierType(_, t1, _) }
pragma[noinline]
private predicate unificationTargetRight(ParameterizedType t2, GenericType g) {
exists(viableMethodImpl(_, _, t2)) and t2.getGenericType() = g
}
private predicate unificationTargets(Type t1, Type t2) {
exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g))
or
exists(Array a1, Array a2 |
unificationTargets(a1, a2) and
t1 = a1.getComponentType() and
t2 = a2.getComponentType()
)
or
exists(ParameterizedType pt1, ParameterizedType pt2, int pos |
unificationTargets(pt1, pt2) and
not pt1.getSourceDeclaration() != pt2.getSourceDeclaration() and
t1 = pt1.getTypeArgument(pos) and
t2 = pt2.getTypeArgument(pos)
)
}
pragma[noinline]
private predicate typeArgsOfUnificationTargets(
ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2
) {
unificationTargets(t1, t2) and
arg1 = t1.getTypeArgument(pos) and
arg2 = t2.getTypeArgument(pos)
}
predicate failsUnification(Type t1, Type t2) {
unificationTargets(t1, t2) and
(
exists(RefType arg1, RefType arg2 |
typeArgsOfUnificationTargets(t1, t2, _, arg1, arg2) and
failsUnification(arg1, arg2)
)
or
failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType())
or
not (
t1 instanceof Array and t2 instanceof Array
or
t1.(PrimitiveType) = t2.(PrimitiveType)
or
t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration()
or
t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration()
or
t1 instanceof BoundedType and t2 instanceof RefType
or
t1 instanceof RefType and t2 instanceof BoundedType
)
)
}
private predicate unificationTargetRight(ParameterizedType t2) {
exists(viableMethodImpl(_, _, t2))
}
private module Unification_v1 =
MkUnification<unificationTargetLeft_v1/1, unificationTargetRight/1>;
private RefType getPreciseType(Expr e) {
result = e.(FunctionalExpr).getConstructedType()
or