Description
Consider the following program:
class A {
void foo(int x) {}
}
abstract class I<T> {
void foo(T x);
}
class B extends A<int> implements I {}
void bar(B b) {
b.foo(1);
}
main() {
bar(new B());
}
This compiles to the following kernel representation:
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
default constructor •() → void
: super core::Object::•()
;
method foo(core::int x) → void {}
}
abstract class I<T extends core::Object> extends core::Object {
default constructor •() → void
: super core::Object::•()
;
abstract method foo(generic-covariant-impl generic-covariant-interface self::I::T x) → void;
}
class B extends self::A implements self::I<dynamic> {
default constructor •() → void
: super self::A::•()
;
forwarding-stub method foo(generic-covariant-impl dynamic x) → void
return super.{self::A::foo}(x);
}
static method bar(self::B b) → void {
b.{self::B::foo}(1);
}
static method main() → dynamic {
self::bar(new self::B::•());
}
Note that the call from bar
to b.foo
has an interface target of self::B::foo
, which is a forwarding stub.
When the analyzer builds its resolution information based on the kernel representation, it can't consider the call to foo
to resolve to self::B::foo
, since there is no such function in the user's source code.
It needs to resolve it to self::A::foo
, the method that would have been the interface target if the forwarding stub hadn't been there. It can't get this information from the body of B::foo
for two reasons:
(1) In a separate compilation scenario, the body of B::foo
may not be present when analyzing bar
; we may only have an outline for B::foo
.
(2) In certain circumstances a forwarding stub is created that lacks a body, e.g.:
abstract class A {
void foo(int x);
}
abstract class I {
void foo(Object x);
}
class B extends A implements I {}
which has the kernel representation:
library;
import self as self;
import "dart:core" as core;
abstract class A extends core::Object {
default constructor •() → void
: super core::Object::•()
;
abstract method foo(core::int x) → void;
}
abstract class I extends core::Object {
default constructor •() → void
: super core::Object::•()
;
abstract method foo(core::Object x) → void;
}
class B extends self::A implements self::I {
default constructor •() → void
: super self::A::•()
;
abstract forwarding-stub method foo(core::Object x) → void;
}
So we need the kernel representation of a forwarding stub to contain a pointer to the interface target that would have been used if the forwarding stub had not been there; and we need this information to be present in the kernel outline. The analyzer can use this information to find the "actual" (non-forwarding) interface target.