C#: Speedup DispatchMethodOrAccessorCall::getAViableOverrider()

In addition to improved performance, the analysis no longer applies a closed-world
assumption to type parameters. That is, if the type of a receiver is a type parameter,
then the call may target any method of a compatible receiver type, not just the
types that actually instantiate the type parameter.
This commit is contained in:
Tom Hvitved
2021-04-14 15:17:34 +02:00
parent 972cc47f67
commit 946fcf1c82

View File

@@ -233,71 +233,61 @@ private module Internal {
}
pragma[noinline]
private predicate hasOverrider(OverridableCallable oc, ValueOrRefType t) {
exists(oc.getAnOverrider(t))
private predicate hasOverrider(OverridableCallable oc, Gvn::GvnType t) {
exists(oc.getAnOverrider(any(ValueOrRefType t0 | Gvn::getGlobalValueNumber(t0) = t)))
}
pragma[noinline]
private predicate hasCallable(OverridableCallable source, ValueOrRefType t, OverridableCallable c) {
private predicate hasCallable(OverridableCallable source, Gvn::GvnType t, OverridableCallable c) {
c.getUnboundDeclaration() = source and
t.hasCallable(c) and
any(ValueOrRefType t0 | Gvn::getGlobalValueNumber(t0) = t).hasCallable(c) and
hasOverrider(c, t) and
hasQualifierTypeOverridden0(t, _) and
hasQualifierTypeOverridden1(source, _)
}
pragma[noinline]
private Unification::ConstrainedTypeParameter getAConstrainedTypeParameterQualifierType(
DispatchMethodOrAccessorCall call
) {
result = getAPossibleType(call.getQualifier(), false)
}
pragma[noinline]
private predicate constrainedTypeParameterQualifierTypeSubsumes(
ValueOrRefType t, Unification::ConstrainedTypeParameter tp
) {
tp = getAConstrainedTypeParameterQualifierType(_) and
tp.subsumes(t)
}
pragma[noinline]
private predicate hasQualifierTypeOverridden0(ValueOrRefType t, DispatchMethodOrAccessorCall call) {
hasOverrider(_, t) and
(
exists(Type t0, Type t1 |
t0 = getAPossibleType(call.getQualifier(), false) and
t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()]
|
t = t1
or
Unification::subsumes(t1, t)
)
or
constrainedTypeParameterQualifierTypeSubsumes(t,
getAConstrainedTypeParameterQualifierType(call))
)
}
pragma[noinline]
private predicate hasQualifierTypeOverridden1(
OverridableCallable c, DispatchMethodOrAccessorCall call
) {
exists(OverridableCallable target | call.getAStaticTarget() = target |
c = target.getUnboundDeclaration()
or
c = target.getAnUltimateImplementor().getUnboundDeclaration()
)
source = any(DispatchMethodOrAccessorCall call).getAStaticTargetExt()
}
abstract private class DispatchMethodOrAccessorCall extends DispatchCallImpl {
pragma[noinline]
OverridableCallable getAStaticTargetExt() {
exists(OverridableCallable target | this.getAStaticTarget() = target |
result = target.getUnboundDeclaration()
or
result = target.getAnUltimateImplementor().getUnboundDeclaration()
)
}
pragma[nomagic]
predicate hasQualifierTypeInherited(Type t) { t = getAPossibleType(this.getQualifier(), _) }
pragma[noinline]
private predicate hasSubsumedQualifierType(Gvn::GvnType t) {
hasOverrider(_, t) and
exists(Type t0 |
t0 = getAPossibleType(this.getQualifier(), false) and
not t0 instanceof TypeParameter
|
t = Gvn::getGlobalValueNumber(t0)
or
Gvn::subsumes(Gvn::getGlobalValueNumber(t0), t)
)
}
pragma[noinline]
private predicate hasConstrainedTypeParameterQualifierType(
Unification::ConstrainedTypeParameter tp
) {
tp = getAPossibleType(this.getQualifier(), false)
}
pragma[noinline]
private predicate hasUnconstrainedTypeParameterQualifierType() {
getAPossibleType(this.getQualifier(), false) instanceof
Unification::UnconstrainedTypeParameter
}
pragma[nomagic]
predicate hasQualifierTypeOverridden(ValueOrRefType t, OverridableCallable c) {
hasQualifierTypeOverridden0(t, this) and
hasCallable(any(OverridableCallable oc | hasQualifierTypeOverridden1(oc, this)), t, c)
predicate hasSubsumedQualifierTypeOverridden(Gvn::GvnType t, OverridableCallable c) {
this.hasSubsumedQualifierType(t) and
hasCallable(any(OverridableCallable oc | oc = this.getAStaticTargetExt()), t, c)
}
/**
@@ -357,12 +347,33 @@ private module Internal {
}
pragma[nomagic]
private Callable getASubsumedStaticTarget0(Type t) {
private predicate contextArgHasConstrainedTypeParameterType(
DispatchCall ctx, Unification::ConstrainedTypeParameter tp
) {
this.contextArgHasType(ctx, tp, false)
}
pragma[nomagic]
private predicate contextArgHasUnconstrainedTypeParameterType(DispatchCall ctx) {
this.contextArgHasType(ctx, any(Unification::UnconstrainedTypeParameter t), false)
}
pragma[nomagic]
private predicate contextArgHasNonTypeParameterType(DispatchCall ctx, Gvn::GvnType t) {
exists(Type t0 |
this.contextArgHasType(ctx, t0, false) and
not t0 instanceof TypeParameter and
t = Gvn::getGlobalValueNumber(t0)
)
}
pragma[nomagic]
private Callable getASubsumedStaticTarget0(Gvn::GvnType t) {
exists(Callable staticTarget, Type declType |
staticTarget = this.getAStaticTarget() and
declType = staticTarget.getDeclaringType() and
result = staticTarget.getUnboundDeclaration() and
Unification::subsumes(declType, t)
Gvn::subsumes(Gvn::getGlobalValueNumber(declType), t)
)
}
@@ -375,7 +386,8 @@ private module Internal {
private Callable getASubsumedStaticTarget() {
result = this.getAStaticTarget()
or
result.getUnboundDeclaration() = this.getASubsumedStaticTarget0(result.getDeclaringType())
result.getUnboundDeclaration() =
this.getASubsumedStaticTarget0(Gvn::getGlobalValueNumber(result.getDeclaringType()))
}
/**
@@ -426,6 +438,12 @@ private module Internal {
)
}
pragma[noinline]
NonConstructedOverridableCallable getAViableOverrider0() {
getAPossibleType(this.getQualifier(), false) instanceof TypeParameter and
result.getAConstructingCallableOrSelf() = this.getAStaticTargetExt()
}
/**
* Gets a callable that is defined in a subtype of the qualifier type of this
* call, and which overrides a static target of this call.
@@ -468,9 +486,22 @@ private module Internal {
*/
private RuntimeCallable getAViableOverrider() {
exists(ValueOrRefType t, NonConstructedOverridableCallable c |
this.hasQualifierTypeOverridden(t, c.getAConstructingCallableOrSelf()) and
this.hasSubsumedQualifierTypeOverridden(Gvn::getGlobalValueNumber(t),
c.getAConstructingCallableOrSelf()) and
result = c.getAnOverrider(t)
)
or
exists(NonConstructedOverridableCallable c |
c = this.getAViableOverrider0() and
result = c.getAnOverrider(_)
|
this.hasUnconstrainedTypeParameterQualifierType()
or
exists(Unification::ConstrainedTypeParameter tp |
this.hasConstrainedTypeParameterQualifierType(tp) and
tp.subsumes(result.getDeclaringType())
)
)
}
override RuntimeCallable getADynamicTarget() {
@@ -510,36 +541,40 @@ private module Internal {
}
pragma[nomagic]
private RuntimeCallable getAViableOverriderInCallContext0(
NonConstructedOverridableCallable c, ValueOrRefType t
) {
result = this.getAViableOverrider() and
this.contextArgHasType(_, _, false) and
result = c.getAnOverrider(t)
private RuntimeCallable getAViableOverriderInCallContext0(Gvn::GvnType t) {
exists(NonConstructedOverridableCallable c |
result = this.getAViableOverrider() and
this.contextArgHasType(_, _, false) and
result = c.getAnOverrider(any(Type t0 | t = Gvn::getGlobalValueNumber(t0))) and
this.getAStaticTarget() = c.getAConstructingCallableOrSelf()
)
}
pragma[nomagic]
private RuntimeCallable getAViableOverriderInCallContext1(
NonConstructedOverridableCallable c, DispatchCall ctx
) {
exists(ValueOrRefType t |
result = this.getAViableOverriderInCallContext0(c, t) and
exists(Type t0, Type t1 |
this.contextArgHasType(ctx, t0, false) and
t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()]
|
t = t1
or
Unification::subsumes(t1, t)
)
private predicate contextArgHasSubsumedType(DispatchCall ctx, Gvn::GvnType t) {
hasOverrider(_, t) and
exists(Gvn::GvnType t0 | this.contextArgHasNonTypeParameterType(ctx, t0) |
t = t0
or
Gvn::subsumes(t0, t)
)
}
pragma[nomagic]
private RuntimeCallable getAViableOverriderInCallContext(DispatchCall ctx) {
exists(NonConstructedOverridableCallable c |
result = this.getAViableOverriderInCallContext1(c, ctx) and
this.getAStaticTarget() = c.getAConstructingCallableOrSelf()
exists(Gvn::GvnType t |
result = this.getAViableOverriderInCallContext0(t) and
this.contextArgHasSubsumedType(ctx, t)
)
or
result = this.getAViableOverrider() and
(
this.contextArgHasUnconstrainedTypeParameterType(ctx)
or
exists(Unification::ConstrainedTypeParameter tp |
this.contextArgHasConstrainedTypeParameterType(ctx, tp) and
tp.subsumes(result.getDeclaringType())
)
)
}