2658f495f2
Change-Id: Ic30aef0302e1d2a2812eeb81b414a69e7591f625 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/149622 Commit-Queue: Bob Nystrom <rnystrom@google.com> Reviewed-by: Nicholas Shahan <nshahan@google.com>
429 lines
12 KiB
Dart
429 lines
12 KiB
Dart
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import "package:expect/expect.dart";
|
|
|
|
// Test various combinations of valid mixin declarations.
|
|
|
|
// Class unrelated to everything else here, just Object with a toString.
|
|
class O {
|
|
String toString() => "O";
|
|
}
|
|
|
|
abstract class A {
|
|
String toString() => "A";
|
|
String methodA() => "A:${this}.A";
|
|
}
|
|
|
|
class B implements A {
|
|
String toString() => "B";
|
|
String methodA() => "B:${this}.A";
|
|
String methodB() => "B:${this}.B";
|
|
}
|
|
|
|
class C extends A {
|
|
String toString() => "C";
|
|
String methodA() => "C:${this}.A->${super.methodA()}";
|
|
String methodC() => "C:${this}.C";
|
|
}
|
|
|
|
class AIJ implements A, I, J {
|
|
String toString() => "AIJ";
|
|
String methodA() => "AIJ:${this}.A";
|
|
String methodI() => "AIJ:${this}.I";
|
|
String methodJ() => "AIJ:${this}.J";
|
|
}
|
|
|
|
class BC extends C implements B {
|
|
String toString() => "BC";
|
|
String methodA() => "BC:${this}.A->${super.methodA()}";
|
|
String methodB() => "BC:${this}.B";
|
|
String methodC() => "BC:${this}.C->${super.methodC()}";
|
|
}
|
|
|
|
// Interfaces.
|
|
abstract class I {
|
|
String methodI();
|
|
}
|
|
abstract class J {
|
|
String methodJ();
|
|
}
|
|
|
|
// Simple mixin with no super-invocations, no super restrictions
|
|
// and no interfaces implemented.
|
|
mixin M {
|
|
String toString() => "?&M";
|
|
String methodM() => "M:${this}.M";
|
|
}
|
|
|
|
|
|
// Mixin which uses the implicit "on Object" to do a super-invocation.
|
|
mixin MO {
|
|
String toString() => "${super.toString()}&MO";
|
|
String methodMO() => "MO:${this}.MO";
|
|
}
|
|
|
|
|
|
// Mixin with "implements" clause.
|
|
mixin MOiIJ implements I, J {
|
|
String toString() => "${super.toString()}&MOiIJ";
|
|
String methodMOiIJ() => "MOiIJ:${this}.MOiIJ";
|
|
String methodI() => "MOiIJ:${this}.I";
|
|
String methodJ() => "MOiIJ:${this}.J";
|
|
}
|
|
|
|
|
|
// Mixin with single non-Object super-constraint.
|
|
mixin MA on A {
|
|
String toString() => "${super.toString()}&MA";
|
|
String methodMA() => "MA:${this}.MA";
|
|
String methodA() => "MA:${this}.A->${super.methodA()}";
|
|
}
|
|
|
|
|
|
// Mixin with super-restriction implementing other interfaces.
|
|
mixin MAiBC on A implements B, C {
|
|
String toString() => "${super.toString()}&MAiBC";
|
|
String methodMAiBC() => "MAiBC:${this}.MAiBC";
|
|
String methodA() => "MAiBC:${this}.A->${super.methodA()}";
|
|
String methodB() => "MAiBC:${this}.B";
|
|
String methodC() => "MAiBC:${this}.C";
|
|
}
|
|
|
|
// Mixin with "implements" clause.
|
|
mixin MBiIJ on B implements I, J {
|
|
String toString() => "${super.toString()}&MBiIJ";
|
|
String methodMOiIJ() => "MBiIJ:${this}.MBiIJ";
|
|
String methodI() => "MBiIJ:${this}.I";
|
|
String methodJ() => "MBiIJ:${this}.J";
|
|
}
|
|
|
|
|
|
// Mixin on more than one class.
|
|
mixin MBC on B, C {
|
|
String toString() => "${super.toString()}&MBC";
|
|
String methodMBC() => "MBC:${this}.MBC";
|
|
String methodA() => "MBC:${this}.A->${super.methodA()}";
|
|
String methodB() => "MBC:${this}.B->${super.methodB()}";
|
|
String methodC() => "MBC:${this}.C->${super.methodC()}";
|
|
}
|
|
|
|
|
|
// One with everything.
|
|
mixin MBCiIJ on B, C implements I, J {
|
|
String toString() => "${super.toString()}&MBCiIJ";
|
|
String methodMBCiIJ() => "MBCiIJ:${this}.MBCiIJ";
|
|
String methodA() => "MBCiIJ:${this}.A->${super.methodA()}";
|
|
String methodB() => "MBCiIJ:${this}.B->${super.methodB()}";
|
|
String methodC() => "MBCiIJ:${this}.C->${super.methodC()}";
|
|
String methodI() => "MBCiIJ:${this}.I";
|
|
String methodJ() => "MBCiIJ:${this}.J";
|
|
}
|
|
|
|
|
|
// Abstract mixin, doesn't implement its interface.
|
|
mixin MiIJ implements I, J {
|
|
String toString() => "${super.toString()}&MiIJ";
|
|
}
|
|
|
|
|
|
// Applications of the mixins.
|
|
|
|
class COaM = O with M;
|
|
|
|
class COaM_2 extends O with M {}
|
|
|
|
class CBaM = B with M;
|
|
|
|
class CBaM_2 extends B with M {}
|
|
|
|
class COaMO = O with MO;
|
|
|
|
class COaMO_2 extends O with MO {}
|
|
|
|
class CBaMO = B with MO;
|
|
|
|
class CBaMO_2 extends B with MO {}
|
|
|
|
class COaMOiIJ = O with MOiIJ;
|
|
|
|
class COaMOiIJ_2 extends O with MOiIJ {}
|
|
|
|
class CBaMBiIJ = B with MBiIJ;
|
|
|
|
class CBaMBiIJ_2 extends B with MBiIJ {}
|
|
|
|
class CAaMA = A with MA;
|
|
|
|
class CAaMA_2 extends A with MA {}
|
|
|
|
class CBaMA = B with MA;
|
|
|
|
class CBaMA_2 extends B with MA {}
|
|
|
|
class CAaMAiBC = A with MAiBC;
|
|
|
|
class CAaMAiBC_2 extends A with MAiBC {}
|
|
|
|
class CBaMAiBC = B with MAiBC;
|
|
|
|
class CBaMAiBC_2 extends B with MAiBC {}
|
|
|
|
class CBCaMBC = BC with MBC;
|
|
|
|
class CBCaMBC_2 extends BC with MBC {}
|
|
|
|
class CAaMAiBCaMBC = CAaMAiBC with MBC;
|
|
|
|
class CAaMAiBCaMBC_2 extends CAaMAiBC with MBC {}
|
|
|
|
class CBCaMBCiIJ = BC with MBCiIJ;
|
|
|
|
class CBCaMBCiIJ_2 extends BC with MBCiIJ {}
|
|
|
|
class CAaMAiBCaMBCiIJ = CAaMAiBC with MBCiIJ;
|
|
|
|
class CAaMAiBCaMBCiIJ_2 extends CAaMAiBC with MBCiIJ {}
|
|
|
|
// Abstract mixin application does not implement I and J.
|
|
abstract class OaMiIJ = O with MiIJ;
|
|
|
|
// Concrete subclass of abstract mixin appliction
|
|
class COaMiIJ extends OaMiIJ {
|
|
String toString() => "${super.toString()}:COaMiIJ";
|
|
String methodI() => "COaMiIJ:${this}.I";
|
|
String methodJ() => "COaMiIJ:${this}.J";
|
|
}
|
|
|
|
// Abstract class with mixin application and does not implement I and J.
|
|
abstract class OaMiIJ_2 extends O with MiIJ {}
|
|
|
|
// Concrete subclass of abstract mixin appliction
|
|
class COaMiIJ_2 extends OaMiIJ_2 {
|
|
String toString() => "${super.toString()}:COaMiIJ";
|
|
String methodI() => "COaMiIJ:${this}.I";
|
|
String methodJ() => "COaMiIJ:${this}.J";
|
|
}
|
|
|
|
// Test of `class C with M` syntax.
|
|
class CwithM with M {}
|
|
class CeOwithM extends Object with M {}
|
|
|
|
// Test that the mixin applications behave as expected.
|
|
void main() {
|
|
{
|
|
for (dynamic o in [COaM(), COaM_2()]) {
|
|
Expect.type<O>(o);
|
|
Expect.type<M>(o);
|
|
Expect.equals("?&M", "$o");
|
|
Expect.equals("M:$o.M", o.methodM());
|
|
}
|
|
}
|
|
|
|
{
|
|
for (dynamic o in [CBaM(), CBaM_2()]) {
|
|
Expect.type<B>(o);
|
|
Expect.type<M>(o);
|
|
Expect.equals("?&M", "$o");
|
|
Expect.equals("B:$o.B", o.methodB());
|
|
Expect.equals("M:$o.M", (o as M).methodM());
|
|
}
|
|
}
|
|
|
|
{
|
|
for (dynamic o in [COaMO(), COaMO_2()]) {
|
|
Expect.type<O>(o);
|
|
Expect.type<MO>(o);
|
|
Expect.equals("O&MO", "$o");
|
|
Expect.equals("MO:$o.MO", o.methodMO());
|
|
}
|
|
}
|
|
|
|
{
|
|
for (dynamic o in [CBaMO(), CBaMO_2()]) {
|
|
Expect.type<B>(o);
|
|
Expect.type<MO>(o);
|
|
Expect.equals("B&MO", "$o");
|
|
Expect.equals("MO:$o.MO", (o as MO).methodMO());
|
|
// Re-assign to cancel out type promotion from previous "as" expression.
|
|
o = o as dynamic;
|
|
Expect.equals("B:$o.B", o.methodB());
|
|
}
|
|
}
|
|
|
|
{
|
|
for (dynamic o in [COaMOiIJ(), COaMOiIJ_2()]) {
|
|
Expect.type<O>(o);
|
|
Expect.type<I>(o);
|
|
Expect.type<J>(o);
|
|
Expect.type<MOiIJ>(o);
|
|
Expect.equals("O&MOiIJ", "$o");
|
|
Expect.equals("MOiIJ:$o.MOiIJ", o.methodMOiIJ());
|
|
Expect.equals("MOiIJ:$o.I", o.methodI());
|
|
Expect.equals("MOiIJ:$o.J", o.methodJ());
|
|
}
|
|
}
|
|
|
|
{
|
|
for (dynamic o in [CBaMBiIJ(), CBaMBiIJ_2()]) {
|
|
Expect.type<B>(o);
|
|
Expect.type<I>(o);
|
|
Expect.type<J>(o);
|
|
Expect.type<MBiIJ>(o);
|
|
Expect.equals("B&MBiIJ", "$o");
|
|
Expect.equals("MBiIJ:$o.MBiIJ", o.methodMOiIJ());
|
|
Expect.equals("B:$o.B", o.methodB());
|
|
Expect.equals("MBiIJ:$o.I", o.methodI());
|
|
Expect.equals("MBiIJ:$o.J", o.methodJ());
|
|
}
|
|
}
|
|
|
|
{
|
|
for (dynamic o in [CAaMA(), CAaMA_2()]) {
|
|
Expect.type<A>(o);
|
|
Expect.type<MA>(o);
|
|
Expect.equals("A&MA", "$o");
|
|
Expect.equals("MA:$o.MA", o.methodMA());
|
|
Expect.equals("MA:$o.A->A:$o.A", o.methodA());
|
|
}
|
|
}
|
|
|
|
{
|
|
for (dynamic o in [CBaMA(), CBaMA_2()]) {
|
|
Expect.type<B>(o);
|
|
Expect.type<MA>(o);
|
|
Expect.equals("B&MA", "$o");
|
|
Expect.equals("MA:$o.MA", (o as MA).methodMA());
|
|
Expect.equals("MA:$o.A->B:$o.A", (o as MA).methodA());
|
|
// Re-assign to cancel out type promotion from previous "as" expression.
|
|
o = o as dynamic;
|
|
Expect.equals("B:$o.B", o.methodB());
|
|
}
|
|
}
|
|
|
|
{
|
|
for (dynamic o in [CAaMAiBC(), CAaMAiBC_2()]) {
|
|
Expect.type<A>(o);
|
|
Expect.type<B>(o);
|
|
Expect.type<C>(o);
|
|
Expect.type<MAiBC>(o);
|
|
Expect.equals("A&MAiBC", "$o");
|
|
Expect.equals("MAiBC:$o.MAiBC", o.methodMAiBC());
|
|
Expect.equals("MAiBC:$o.A->A:$o.A", o.methodA());
|
|
Expect.equals("MAiBC:$o.B", o.methodB());
|
|
Expect.equals("MAiBC:$o.C", o.methodC());
|
|
}
|
|
}
|
|
|
|
{
|
|
for (dynamic o in [CBaMAiBC(), CBaMAiBC_2()]) {
|
|
Expect.type<A>(o);
|
|
Expect.type<B>(o);
|
|
Expect.type<C>(o);
|
|
Expect.type<MAiBC>(o);
|
|
Expect.equals("B&MAiBC", "$o");
|
|
Expect.equals("MAiBC:$o.MAiBC", o.methodMAiBC());
|
|
Expect.equals("MAiBC:$o.A->B:$o.A", o.methodA());
|
|
Expect.equals("MAiBC:$o.B", o.methodB());
|
|
Expect.equals("MAiBC:$o.C", o.methodC());
|
|
}
|
|
}
|
|
|
|
{
|
|
for (dynamic o in [CBCaMBC(), CBCaMBC_2()]) {
|
|
Expect.type<BC>(o);
|
|
Expect.type<MBC>(o);
|
|
Expect.equals("BC&MBC", "$o");
|
|
Expect.equals("MBC:$o.MBC", o.methodMBC());
|
|
Expect.equals("MBC:$o.A->BC:$o.A->C:$o.A->A:$o.A", o.methodA());
|
|
Expect.equals("MBC:$o.B->BC:$o.B", o.methodB());
|
|
Expect.equals("MBC:$o.C->BC:$o.C->C:$o.C", o.methodC());
|
|
}
|
|
}
|
|
|
|
{
|
|
// Mixin on top of mixin application.
|
|
for (dynamic o in [CAaMAiBCaMBC(), CAaMAiBCaMBC_2()]) {
|
|
Expect.type<CAaMAiBC>(o);
|
|
Expect.type<MBC>(o);
|
|
Expect.equals("A&MAiBC&MBC", "$o");
|
|
Expect.equals("MBC:$o.MBC", (o as MBC).methodMBC());
|
|
// Re-assign to cancel out type promotion from previous "as" expression.
|
|
o = o as dynamic;
|
|
Expect.equals("MAiBC:$o.MAiBC", o.methodMAiBC());
|
|
Expect.equals("MBC:$o.A->MAiBC:$o.A->A:$o.A", o.methodA());
|
|
Expect.equals("MBC:$o.B->MAiBC:$o.B", o.methodB());
|
|
Expect.equals("MBC:$o.C->MAiBC:$o.C", o.methodC());
|
|
}
|
|
}
|
|
|
|
{
|
|
for (dynamic o in [CBCaMBCiIJ(), CBCaMBCiIJ_2()]) {
|
|
Expect.type<BC>(o);
|
|
Expect.type<MBCiIJ>(o);
|
|
Expect.type<I>(o);
|
|
Expect.type<J>(o);
|
|
Expect.equals("BC&MBCiIJ", "$o");
|
|
Expect.equals("MBCiIJ:$o.MBCiIJ", o.methodMBCiIJ());
|
|
Expect.equals("MBCiIJ:$o.A->BC:$o.A->C:$o.A->A:$o.A", o.methodA());
|
|
Expect.equals("MBCiIJ:$o.B->BC:$o.B", o.methodB());
|
|
Expect.equals("MBCiIJ:$o.C->BC:$o.C->C:$o.C", o.methodC());
|
|
Expect.equals("MBCiIJ:$o.I", o.methodI());
|
|
Expect.equals("MBCiIJ:$o.J", o.methodJ());
|
|
}
|
|
}
|
|
|
|
{
|
|
// Mixin on top of mixin application.
|
|
for (dynamic o in [CAaMAiBCaMBCiIJ(), CAaMAiBCaMBCiIJ_2()]) {
|
|
Expect.type<CAaMAiBC>(o);
|
|
Expect.type<MBCiIJ>(o);
|
|
Expect.type<I>(o);
|
|
Expect.type<J>(o);
|
|
Expect.equals("A&MAiBC&MBCiIJ", "$o");
|
|
Expect.equals("MBCiIJ:$o.MBCiIJ", o.methodMBCiIJ());
|
|
Expect.equals("MAiBC:$o.MAiBC", (o as CAaMAiBC).methodMAiBC());
|
|
// Re-assign to cancel out type promotion from previous "as" expression.
|
|
o = o as dynamic;
|
|
Expect.equals("MBCiIJ:$o.A->MAiBC:$o.A->A:$o.A", o.methodA());
|
|
Expect.equals("MBCiIJ:$o.B->MAiBC:$o.B", o.methodB());
|
|
Expect.equals("MBCiIJ:$o.C->MAiBC:$o.C", o.methodC());
|
|
Expect.equals("MBCiIJ:$o.I", o.methodI());
|
|
Expect.equals("MBCiIJ:$o.J", o.methodJ());
|
|
}
|
|
}
|
|
|
|
{
|
|
// Abstract mixin application, concrete subclass.
|
|
for (dynamic o in [COaMiIJ(), COaMiIJ_2()]) {
|
|
Expect.type<O>(o);
|
|
Expect.type<MiIJ>(o);
|
|
Expect.isTrue(o is OaMiIJ || o is OaMiIJ_2,
|
|
"`$o` should subtype OaMiIJ or OaMiIJ_2");
|
|
Expect.type<I>(o);
|
|
Expect.type<J>(o);
|
|
Expect.equals("O&MiIJ:COaMiIJ", "$o");
|
|
Expect.equals("COaMiIJ:$o.I", o.methodI());
|
|
Expect.equals("COaMiIJ:$o.J", o.methodJ());
|
|
}
|
|
}
|
|
|
|
Expect.equals(CeOwithM().toString(), CwithM().toString());
|
|
|
|
{
|
|
// Regression test for private fields.
|
|
var c = PrivateFieldClass();
|
|
Expect.equals(42, c._foo);
|
|
}
|
|
}
|
|
|
|
|
|
mixin PrivateFieldMixin {
|
|
int _foo = 40;
|
|
}
|
|
|
|
class PrivateFieldClass with PrivateFieldMixin {
|
|
int get _foo => super._foo + 2;
|
|
}
|