mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Kotlin's implementation of defaults depends on the -Xjvm-default setting (or the @JvmDefault deprecated annotation, not implemented here): by default, actual interface class files don't use default method, and any class that would inherit one instead implements the interface calling a static method defined on TheInterface$DefaultImpls. With
-Xjvm-default=all or =all-compatibility, real interface default methods are emitted, with the latter retaining the DefaultImpls methods so that other Kotlin can use it.
Here I adopt a hybrid solution: create a real default method implementation, but also emit a forwarding method like `@override int f(int x) { return super.TheInterface.f(x); }`, because the Java extractor will see `MyClass.f` in the emitted class file and try to dispatch directly to it. The only downside is that we emit a default interface
method body for a prototype that will appear to be `abstract` to the Java extractor and which it will extract as such. I work around this by tolerating the combination `default abstract` in QL. The alternative would be to fully mimic the DefaultImpls approach, giving 100% fidelity to kotlinc's strategy and therefore no clash with the Java
extractor's view of the world.
21 lines
586 B
Java
21 lines
586 B
Java
public class User {
|
|
|
|
public static void sink(int x) { }
|
|
|
|
// Real is compiled with synthetic interface method forwarders, so it appears from a Java perspective to override the interface Test.
|
|
// RealNoForwards is compiled with -Xjvm-default=all, meaning real Java 8 default interface methods are used, no synthetic forwarders
|
|
// are created, and call resolution should go straight to the default.
|
|
public static void test(Real r1, RealNoForwards r2) {
|
|
|
|
sink(r1.f());
|
|
sink(r1.g(2));
|
|
sink(r1.getX());
|
|
|
|
sink(r2.f());
|
|
sink(r2.g(5));
|
|
sink(r2.getX());
|
|
|
|
}
|
|
|
|
}
|