mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Implement Kotlin default interface method forwarding
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.
This commit is contained in:
@@ -67,6 +67,8 @@ class Element extends @element, Top {
|
||||
i = 9 and result = "Forwarder for a @JvmOverloads-annotated function"
|
||||
or
|
||||
i = 10 and result = "Forwarder for Kotlin calls that need default arguments filling in"
|
||||
or
|
||||
i = 11 and result = "Forwarder for a Kotlin class inheriting an interface default method"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,7 +471,12 @@ class Method extends Callable, @method {
|
||||
}
|
||||
|
||||
override predicate isAbstract() {
|
||||
Callable.super.isAbstract()
|
||||
// The combination `abstract default` isn't legal in Java,
|
||||
// but it occurs when the Kotlin extractor records a default
|
||||
// body, but the output class file in fact uses an abstract
|
||||
// method and an associated static helper, which we don't
|
||||
// extract as an implementation detail.
|
||||
Callable.super.isAbstract() and not this.isDefault()
|
||||
or
|
||||
// JLS 9.4: An interface method lacking a `private`, `default`, or `static` modifier
|
||||
// is implicitly abstract.
|
||||
|
||||
Reference in New Issue
Block a user