mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:49:00 +00:00
implement super mixins in dartdevc and fix a few issues in Analyzer
Fixes #34167. This implements the Dart 2 mixin proposal (https://goo.gl/KEKQyv) for DDC. When the mixin is applied, a class is created for the application that extends the correct superclass and has all of the instance members, so `super` works correctly. This also fixes a few minor issues in Analyzer's (mostly complete) implementation: - InterfaceType.isObject now returns false for Dart 2 mixins. - Least upper bound calculation recognizes mixins are not Object. - Interface of the mixin now implements its superclass constraints. - Mixin superclass constraints are checked against the superclass and all previously applied mixins (if any); this keeps it working with the subtype fix above, and also prevents a not-yet-applied mixin from satisfying the constraint The language_2/mixin_declaration tests were updated with a few minor fixes now that we can run Analyzer/dartdevc to test them. This change implements super mixins for DDC's Kernel backend (DDK) too. This will be enabled once Kernel adds a flag to recognize which Class nodes are mixins (vs normal classes). Change-Id: Ib3c4fcb12de9988345e52d92931196828d8227c3 Reviewed-on: https://dart-review.googlesource.com/74965 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Vijay Menon <vsm@google.com>
This commit is contained in:
parent
0471d7e3a1
commit
0496569f09
24 changed files with 362 additions and 143 deletions
|
@ -626,8 +626,8 @@ class ClassElementImpl extends AbstractClassElementImpl
|
|||
return false;
|
||||
}
|
||||
if (supertype == null) {
|
||||
// Should never happen, since Object is the only class that has no
|
||||
// supertype, and it should have been caught by the test above.
|
||||
// Should never happen, since Object and mixins are the only classes that
|
||||
// have no supertype, and they should have been caught by the test above.
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
@ -647,8 +647,8 @@ class ClassElementImpl extends AbstractClassElementImpl
|
|||
}
|
||||
classesSeen.add(nearestNonMixinClass);
|
||||
if (nearestNonMixinClass.supertype == null) {
|
||||
// Should never happen, since Object is the only class that has no
|
||||
// supertype, and it is not a mixin application.
|
||||
// Should never happen, since Object and mixins are the only classes that
|
||||
// have no supertype, and they are not mixin applications.
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
@ -1106,9 +1106,9 @@ class ClassElementImpl extends AbstractClassElementImpl
|
|||
// forwarded to this class.
|
||||
Iterable<ConstructorElement> constructorsToForward;
|
||||
if (supertype == null) {
|
||||
// Shouldn't ever happen, since the only class with no supertype is
|
||||
// Object, and it isn't a mixin application. But for safety's sake just
|
||||
// assume an empty list.
|
||||
// Shouldn't ever happen, since the only classes with no supertype are
|
||||
// Object and mixins, and they aren't a mixin application. But for
|
||||
// safety's sake just assume an empty list.
|
||||
assert(false);
|
||||
constructorsToForward = <ConstructorElement>[];
|
||||
} else if (!supertype.element.isMixinApplication) {
|
||||
|
|
|
@ -1339,7 +1339,7 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
|
|||
}
|
||||
|
||||
@override
|
||||
bool get isObject => element.supertype == null;
|
||||
bool get isObject => element.supertype == null && !element.isMixin;
|
||||
|
||||
@override
|
||||
List<MethodElement> get methods {
|
||||
|
@ -1495,10 +1495,9 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
|
|||
ClassElement jElement = j.element;
|
||||
InterfaceType supertype = jElement.supertype;
|
||||
//
|
||||
// If J has no direct supertype then it is Object, and Object has no direct
|
||||
// supertypes.
|
||||
// If J is Object, then it has no direct supertypes.
|
||||
//
|
||||
if (supertype == null) {
|
||||
if (j.isObject) {
|
||||
return false;
|
||||
}
|
||||
//
|
||||
|
@ -2212,7 +2211,7 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
|
|||
InterfaceType type, int depth, HashSet<ClassElement> visitedTypes) {
|
||||
ClassElement classElement = type.element;
|
||||
// Object case
|
||||
if (classElement.supertype == null || visitedTypes.contains(classElement)) {
|
||||
if (type.isObject || visitedTypes.contains(classElement)) {
|
||||
return depth;
|
||||
}
|
||||
int longestPath = 1;
|
||||
|
@ -2244,10 +2243,12 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
|
|||
// TODO(brianwilkerson) Does this also need to add in the number of mixin
|
||||
// classes?
|
||||
InterfaceType supertype = classElement.supertype;
|
||||
pathLength = _computeLongestInheritancePathToObject(
|
||||
supertype, depth + 1, visitedTypes);
|
||||
if (pathLength > longestPath) {
|
||||
longestPath = pathLength;
|
||||
if (supertype != null) {
|
||||
pathLength = _computeLongestInheritancePathToObject(
|
||||
supertype, depth + 1, visitedTypes);
|
||||
if (pathLength > longestPath) {
|
||||
longestPath = pathLength;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
visitedTypes.remove(classElement);
|
||||
|
|
|
@ -283,7 +283,7 @@ class InheritanceManager {
|
|||
}
|
||||
InterfaceType supertype = classElt.supertype;
|
||||
if (supertype == null) {
|
||||
// classElt is Object
|
||||
// classElt is Object or a mixin
|
||||
_classLookup[classElt] = resultMap;
|
||||
return resultMap;
|
||||
}
|
||||
|
@ -380,9 +380,8 @@ class InheritanceManager {
|
|||
// functionality in InheritanceManagerTest
|
||||
chain.add(currentType);
|
||||
ClassElement classElt = currentType.element;
|
||||
InterfaceType supertype = classElt.supertype;
|
||||
// Base case- reached Object
|
||||
if (supertype == null) {
|
||||
if (currentType.isObject) {
|
||||
// Looked up the chain all the way to Object, return null.
|
||||
// This should never happen.
|
||||
return;
|
||||
|
@ -411,8 +410,9 @@ class InheritanceManager {
|
|||
}
|
||||
}
|
||||
// Superclass
|
||||
ClassElement superclassElt = supertype.element;
|
||||
if (lookupMember(superclassElt, memberName) != null) {
|
||||
InterfaceType supertype = classElt.supertype;
|
||||
if (supertype != null &&
|
||||
lookupMember(supertype.element, memberName) != null) {
|
||||
_computeInheritancePath(chain, supertype, memberName);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -719,8 +719,9 @@ class ElementResolver extends SimpleAstVisitor<Object> {
|
|||
// Generate the type name.
|
||||
// The error code will never be generated via type propagation
|
||||
DartType getSuperType(DartType type) {
|
||||
if (type is InterfaceType && !type.isObject) {
|
||||
return type.superclass;
|
||||
if (type is InterfaceType) {
|
||||
InterfaceType superclass = type.superclass;
|
||||
if (superclass != null) return superclass;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
|
|
@ -1910,7 +1910,9 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
|
|||
return false;
|
||||
}
|
||||
bool problemReported = false;
|
||||
for (TypeName mixinName in withClause.mixinTypes) {
|
||||
List<TypeName> mixinTypes = withClause.mixinTypes;
|
||||
for (int i = 0; i < mixinTypes.length; i++) {
|
||||
TypeName mixinName = mixinTypes[i];
|
||||
DartType mixinType = mixinName.type;
|
||||
if (mixinType is InterfaceType) {
|
||||
if (_checkForExtendsOrImplementsDisallowedClass(
|
||||
|
@ -1923,7 +1925,8 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
|
|||
problemReported = true;
|
||||
}
|
||||
if (mixinElement.isMixin) {
|
||||
if (_checkForMixinSuperclassConstraints(mixinName)) {
|
||||
if (_checkForMixinSuperclassConstraints(
|
||||
mixinName, mixinTypes.take(i))) {
|
||||
problemReported = true;
|
||||
}
|
||||
if (_checkForMixinSuperInvokedMembers(mixinName, mixinElement)) {
|
||||
|
@ -4256,12 +4259,14 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
|
|||
}
|
||||
|
||||
/// Check that superclass constrains for the mixin type of [mixinName]
|
||||
/// are satisfied by the [_enclosingClass].
|
||||
bool _checkForMixinSuperclassConstraints(TypeName mixinName) {
|
||||
InterfaceType enclosingType = _enclosingClass.type;
|
||||
/// are satisfied by the superclass and/or any previous mixin applications.
|
||||
bool _checkForMixinSuperclassConstraints(
|
||||
TypeName mixinName, Iterable<TypeName> previousMixins) {
|
||||
List<InterfaceType> supertypes = [_enclosingClass.supertype];
|
||||
supertypes.addAll(previousMixins.map((t) => t.type));
|
||||
InterfaceType mixinType = mixinName.type;
|
||||
for (var constraint in mixinType.superclassConstraints) {
|
||||
if (!_typeSystem.isSubtypeOf(enclosingType, constraint)) {
|
||||
if (!supertypes.any((s) => _typeSystem.isSubtypeOf(s, constraint))) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.MIXIN_APPLICATION_NOT_IMPLEMENTED_INTERFACE,
|
||||
mixinName.name,
|
||||
|
|
|
@ -1582,7 +1582,9 @@ class StrongTypeSystemImpl extends TypeSystem {
|
|||
visitedTypes ??= new HashSet<ClassElement>();
|
||||
if (!visitedTypes.add(i1Element)) return false;
|
||||
|
||||
if (_isInterfaceSubtypeOf(i1.superclass, i2, visitedTypes)) {
|
||||
InterfaceType superclass = i1.superclass;
|
||||
if (superclass != null &&
|
||||
_isInterfaceSubtypeOf(superclass, i2, visitedTypes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1598,6 +1600,14 @@ class StrongTypeSystemImpl extends TypeSystem {
|
|||
}
|
||||
}
|
||||
|
||||
if (i1Element.isMixin) {
|
||||
for (final parent in i1.superclassConstraints) {
|
||||
if (_isInterfaceSubtypeOf(parent, i2, visitedTypes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -971,7 +971,9 @@ class CodeGenerator extends Object
|
|||
_emitVirtualFieldSymbols(classElem, body);
|
||||
_emitClassSignature(classElem, className, memberMap, body);
|
||||
_initExtensionSymbols(classElem);
|
||||
_defineExtensionMembers(className, body);
|
||||
if (!classElem.isMixin) {
|
||||
_defineExtensionMembers(className, body);
|
||||
}
|
||||
_emitClassMetadata(classNode.metadata, className, body);
|
||||
|
||||
var classDef = JS.Statement.from(body);
|
||||
|
@ -1233,7 +1235,7 @@ class CodeGenerator extends Object
|
|||
|
||||
@override
|
||||
JS.Statement visitMixinDeclaration(MixinDeclaration node) {
|
||||
throw new UnimplementedError();
|
||||
return _emitClassDeclaration(node, node.declaredElement, node.members);
|
||||
}
|
||||
|
||||
/// Wraps a possibly generic class in its type arguments.
|
||||
|
@ -1275,6 +1277,70 @@ class CodeGenerator extends Object
|
|||
return js.statement('# = #;', [className, classExpr]);
|
||||
}
|
||||
|
||||
/// Like [_emitClassStatement] but emits a Dart 2.1 mixin represented by
|
||||
/// [classElem].
|
||||
///
|
||||
/// Mixins work similar to normal classes, but their instance methods close
|
||||
/// over the actual superclass. Given a Dart class like:
|
||||
///
|
||||
/// mixin M on C {
|
||||
/// foo() => super.foo() + 42;
|
||||
/// }
|
||||
///
|
||||
/// We generate a JS class like this:
|
||||
///
|
||||
/// lib.M = class M extends core.Object {}
|
||||
/// lib.M[dart.mixinOn] = (C) => class M extends C {
|
||||
/// foo() {
|
||||
/// return super.foo() + 42;
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
/// The special `dart.mixinOn` symbolized property is used by the runtime
|
||||
/// helper `dart.applyMixin`. The helper calls the function with the actual
|
||||
/// base class, and then copies the resulting members to the destination
|
||||
/// class.
|
||||
///
|
||||
/// In the long run we may be able to improve this so we do not have the
|
||||
/// unnecessary class, but for now, this lets us get the right semantics with
|
||||
/// minimal compiler and runtime changes.
|
||||
void _emitMixinStatement(
|
||||
ClassElement classElem,
|
||||
JS.Expression className,
|
||||
JS.Expression heritage,
|
||||
List<JS.Method> methods,
|
||||
List<JS.Statement> body) {
|
||||
assert(classElem.isMixin);
|
||||
|
||||
var staticMethods = methods.where((m) => m.isStatic).toList();
|
||||
var instanceMethods = methods.where((m) => !m.isStatic).toList();
|
||||
body.add(
|
||||
_emitClassStatement(classElem, className, heritage, staticMethods));
|
||||
|
||||
var superclassId = JS.TemporaryId(
|
||||
classElem.superclassConstraints.map((t) => t.name).join('_'));
|
||||
var classId =
|
||||
className is JS.Identifier ? className : JS.TemporaryId(classElem.name);
|
||||
|
||||
var mixinMemberClass =
|
||||
JS.ClassExpression(classId, superclassId, instanceMethods);
|
||||
|
||||
JS.Node arrowFnBody = mixinMemberClass;
|
||||
var extensionInit = <JS.Statement>[];
|
||||
_defineExtensionMembers(classId, extensionInit);
|
||||
if (extensionInit.isNotEmpty) {
|
||||
extensionInit.insert(0, mixinMemberClass.toStatement());
|
||||
extensionInit.add(classId.toReturn());
|
||||
arrowFnBody = JS.Block(extensionInit);
|
||||
}
|
||||
|
||||
body.add(js.statement('#[#.mixinOn] = #', [
|
||||
className,
|
||||
runtimeModule,
|
||||
JS.ArrowFun([superclassId], arrowFnBody)
|
||||
]));
|
||||
}
|
||||
|
||||
void _defineClass(
|
||||
ClassElement classElem,
|
||||
JS.Expression className,
|
||||
|
@ -1305,7 +1371,9 @@ class CodeGenerator extends Object
|
|||
if (t.typeArguments.any(defer)) return true;
|
||||
if (t is InterfaceType) {
|
||||
var e = t.element;
|
||||
return e.mixins.any(defer) || defer(e.supertype);
|
||||
if (e.mixins.any(defer)) return true;
|
||||
var supertype = e.supertype;
|
||||
return supertype != null && defer(supertype);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -1328,7 +1396,7 @@ class CodeGenerator extends Object
|
|||
return base;
|
||||
}
|
||||
|
||||
var supertype = classElem.supertype;
|
||||
var supertype = classElem.isMixin ? types.objectType : classElem.supertype;
|
||||
var hasUnnamedSuper = _hasUnnamedConstructor(supertype.element);
|
||||
|
||||
void emitMixinConstructors(JS.Expression className, [InterfaceType mixin]) {
|
||||
|
@ -1385,7 +1453,7 @@ class CodeGenerator extends Object
|
|||
var classExpr = deferMixin ? getBaseClass(0) : className;
|
||||
|
||||
mixinBody
|
||||
.add(runtimeStatement('mixinMembers(#, #)', [classExpr, mixinClass]));
|
||||
.add(runtimeStatement('applyMixin(#, #)', [classExpr, mixinClass]));
|
||||
|
||||
_topLevelClass = savedTopLevel;
|
||||
|
||||
|
@ -1395,8 +1463,8 @@ class CodeGenerator extends Object
|
|||
//
|
||||
// We do this with the following pattern:
|
||||
//
|
||||
// mixinMembers(C, class C$ extends M { <methods> });
|
||||
mixinBody.add(runtimeStatement('mixinMembers(#, #)', [
|
||||
// applyMixin(C, class C$ extends M { <methods> });
|
||||
mixinBody.add(runtimeStatement('applyMixin(#, #)', [
|
||||
classExpr,
|
||||
JS.ClassExpression(
|
||||
JS.TemporaryId(classElem.name), mixinClass, methods)
|
||||
|
@ -1428,11 +1496,11 @@ class CodeGenerator extends Object
|
|||
hasUnnamedSuper = hasUnnamedSuper || _hasUnnamedConstructor(m.element);
|
||||
|
||||
if (shouldDefer(m)) {
|
||||
deferredSupertypes.add(runtimeStatement('mixinMembers(#, #)',
|
||||
deferredSupertypes.add(runtimeStatement('applyMixin(#, #)',
|
||||
[getBaseClass(mixinLength - i), emitDeferredType(m)]));
|
||||
} else {
|
||||
body.add(
|
||||
runtimeStatement('mixinMembers(#, #)', [mixinId, emitClassRef(m)]));
|
||||
runtimeStatement('applyMixin(#, #)', [mixinId, emitClassRef(m)]));
|
||||
}
|
||||
|
||||
baseClass = mixinId;
|
||||
|
@ -1440,7 +1508,14 @@ class CodeGenerator extends Object
|
|||
|
||||
_topLevelClass = savedTopLevel;
|
||||
|
||||
body.add(_emitClassStatement(classElem, className, baseClass, methods));
|
||||
if (classElem.isMixin) {
|
||||
// TODO(jmesserly): we could make this more efficient, as this creates
|
||||
// an extra unnecessary class. But it's the easiest way to handle the
|
||||
// current system.
|
||||
_emitMixinStatement(classElem, className, baseClass, methods, body);
|
||||
} else {
|
||||
body.add(_emitClassStatement(classElem, className, baseClass, methods));
|
||||
}
|
||||
|
||||
if (classElem.isMixinApplication) emitMixinConstructors(className);
|
||||
}
|
||||
|
@ -2069,9 +2144,13 @@ class CodeGenerator extends Object
|
|||
/// Emit the signature on the class recording the runtime type information
|
||||
void _emitClassSignature(ClassElement classElem, JS.Expression className,
|
||||
Map<Element, Declaration> annotatedMembers, List<JS.Statement> body) {
|
||||
if (classElem.interfaces.isNotEmpty) {
|
||||
if (classElem.interfaces.isNotEmpty ||
|
||||
classElem.superclassConstraints.isNotEmpty) {
|
||||
var interfaces = classElem.interfaces.toList()
|
||||
..addAll(classElem.superclassConstraints);
|
||||
|
||||
body.add(js.statement('#[#.implements] = () => [#];',
|
||||
[className, runtimeModule, classElem.interfaces.map(_emitType)]));
|
||||
[className, runtimeModule, interfaces.map(_emitType)]));
|
||||
}
|
||||
|
||||
void emitSignature(String name, List<JS.Property> elements) {
|
||||
|
@ -2363,7 +2442,9 @@ class CodeGenerator extends Object
|
|||
// Get the supertype's unnamed constructor.
|
||||
superCtor ??= element.supertype?.element?.unnamedConstructor;
|
||||
if (superCtor == null) {
|
||||
assert(element.type.isObject || options.unsafeForceCompile);
|
||||
assert(element.type.isObject ||
|
||||
element.isMixin ||
|
||||
options.unsafeForceCompile);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -2384,6 +2465,7 @@ class CodeGenerator extends Object
|
|||
|
||||
bool _hasUnnamedSuperConstructor(ClassElement e) {
|
||||
var supertype = e.supertype;
|
||||
// Object or mixin declaration.
|
||||
if (supertype == null) return false;
|
||||
if (_hasUnnamedConstructor(supertype.element)) return true;
|
||||
for (var mixin in e.mixins) {
|
||||
|
|
|
@ -139,6 +139,7 @@ List<ClassElement> getSuperclasses(ClassElement cls) {
|
|||
if (mixin != null) result.add(mixin);
|
||||
}
|
||||
var supertype = cls.supertype;
|
||||
// Object or mixin declaration.
|
||||
if (supertype == null) break;
|
||||
|
||||
cls = supertype.element;
|
||||
|
|
|
@ -103,7 +103,8 @@ class ExtensionTypeSet {
|
|||
}
|
||||
element.interfaces.forEach(_addExtensionType);
|
||||
element.mixins.forEach(_addExtensionType);
|
||||
_addExtensionType(element.supertype);
|
||||
var supertype = element.supertype;
|
||||
if (supertype != null) _addExtensionType(element.supertype);
|
||||
}
|
||||
|
||||
void _addExtensionTypesForLibrary(String libraryUri, List<String> typeNames) {
|
||||
|
|
|
@ -264,7 +264,7 @@ class ClassPropertyModel {
|
|||
// mock methods encodes information about the number of arguments (and type
|
||||
// arguments) that D expects.
|
||||
var element = type.element;
|
||||
if (!hasNoSuchMethod(element)) return;
|
||||
if (element.isMixin || !hasNoSuchMethod(element)) return;
|
||||
|
||||
// Collect all unimplemented members.
|
||||
//
|
||||
|
@ -409,7 +409,8 @@ class ClassPropertyModel {
|
|||
for (var i in element.interfaces) {
|
||||
_collectNativeMembers(i, members);
|
||||
}
|
||||
if (!type.isObject) {
|
||||
var supertype = element.supertype;
|
||||
if (supertype != null) {
|
||||
_collectNativeMembers(element.supertype, members);
|
||||
}
|
||||
if (element.isEnum) {
|
||||
|
|
|
@ -621,8 +621,15 @@ class MiniJsParser {
|
|||
static final ARROW_TOKEN = '=>';
|
||||
static final ELLIPSIS_TOKEN = '...';
|
||||
|
||||
static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS =
|
||||
['typeof', 'void', 'delete', 'in', 'instanceof', 'await'].toSet();
|
||||
static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS = [
|
||||
'typeof',
|
||||
'void',
|
||||
'delete',
|
||||
'in',
|
||||
'instanceof',
|
||||
'await',
|
||||
'extends'
|
||||
].toSet();
|
||||
|
||||
static int category(int code) {
|
||||
if (code >= CATEGORIES.length) return OTHER;
|
||||
|
|
|
@ -559,7 +559,9 @@ class ProgramCompiler extends Object
|
|||
_emitVirtualFieldSymbols(c, body);
|
||||
_emitClassSignature(c, className, body);
|
||||
_initExtensionSymbols(c);
|
||||
_defineExtensionMembers(className, body);
|
||||
if (!isMixinDeclaration(c)) {
|
||||
_defineExtensionMembers(className, body);
|
||||
}
|
||||
_emitClassMetadata(c.annotations, className, body);
|
||||
|
||||
var classDef = JS.Statement.from(body);
|
||||
|
@ -619,6 +621,67 @@ class ProgramCompiler extends Object
|
|||
return js.statement('# = #;', [className, classExpr]);
|
||||
}
|
||||
|
||||
/// Like [_emitClassStatement] but emits a Dart 2.1 mixin represented by
|
||||
/// [c].
|
||||
///
|
||||
/// Mixins work similar to normal classes, but their instance methods close
|
||||
/// over the actual superclass. Given a Dart class like:
|
||||
///
|
||||
/// mixin M on C {
|
||||
/// foo() => super.foo() + 42;
|
||||
/// }
|
||||
///
|
||||
/// We generate a JS class like this:
|
||||
///
|
||||
/// lib.M = class M extends core.Object {}
|
||||
/// lib.M[dart.mixinOn] = (C) => class M extends C {
|
||||
/// foo() {
|
||||
/// return super.foo() + 42;
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
/// The special `dart.mixinOn` symbolized property is used by the runtime
|
||||
/// helper `dart.applyMixin`. The helper calls the function with the actual
|
||||
/// base class, and then copies the resulting members to the destination
|
||||
/// class.
|
||||
///
|
||||
/// In the long run we may be able to improve this so we do not have the
|
||||
/// unnecessary class, but for now, this lets us get the right semantics with
|
||||
/// minimal compiler and runtime changes.
|
||||
void _emitMixinStatement(
|
||||
Class c,
|
||||
JS.Expression className,
|
||||
JS.Expression heritage,
|
||||
List<JS.Method> methods,
|
||||
List<JS.Statement> body) {
|
||||
var staticMethods = methods.where((m) => m.isStatic).toList();
|
||||
var instanceMethods = methods.where((m) => !m.isStatic).toList();
|
||||
|
||||
body.add(_emitClassStatement(c, className, heritage, staticMethods));
|
||||
var superclassId = JS.TemporaryId(getLocalClassName(c.superclass));
|
||||
var classId = className is JS.Identifier
|
||||
? className
|
||||
: JS.TemporaryId(getLocalClassName(c));
|
||||
|
||||
var mixinMemberClass =
|
||||
JS.ClassExpression(classId, superclassId, instanceMethods);
|
||||
|
||||
JS.Node arrowFnBody = mixinMemberClass;
|
||||
var extensionInit = <JS.Statement>[];
|
||||
_defineExtensionMembers(classId, extensionInit);
|
||||
if (extensionInit.isNotEmpty) {
|
||||
extensionInit.insert(0, mixinMemberClass.toStatement());
|
||||
extensionInit.add(classId.toReturn());
|
||||
arrowFnBody = JS.Block(extensionInit);
|
||||
}
|
||||
|
||||
body.add(js.statement('#[#.mixinOn] = #', [
|
||||
className,
|
||||
runtimeModule,
|
||||
JS.ArrowFun([superclassId], arrowFnBody)
|
||||
]));
|
||||
}
|
||||
|
||||
void _defineClass(Class c, JS.Expression className, List<JS.Method> methods,
|
||||
List<JS.Statement> body, List<JS.Statement> deferredSupertypes) {
|
||||
if (c == coreTypes.objectClass) {
|
||||
|
@ -746,7 +809,7 @@ class ProgramCompiler extends Object
|
|||
var classExpr = deferMixin ? getBaseClass(0) : className;
|
||||
|
||||
mixinBody
|
||||
.add(runtimeStatement('mixinMembers(#, #)', [classExpr, mixinClass]));
|
||||
.add(runtimeStatement('applyMixin(#, #)', [classExpr, mixinClass]));
|
||||
|
||||
if (methods.isNotEmpty) {
|
||||
// However we may need to add some methods to this class that call
|
||||
|
@ -754,8 +817,8 @@ class ProgramCompiler extends Object
|
|||
//
|
||||
// We do this with the following pattern:
|
||||
//
|
||||
// mixinMembers(C, class C$ extends M { <methods> });
|
||||
mixinBody.add(runtimeStatement('mixinMembers(#, #)', [
|
||||
// applyMixin(C, class C$ extends M { <methods> });
|
||||
mixinBody.add(runtimeStatement('applyMixin(#, #)', [
|
||||
classExpr,
|
||||
JS.ClassExpression(
|
||||
JS.TemporaryId(getLocalClassName(c)), mixinClass, methods)
|
||||
|
@ -790,17 +853,22 @@ class ProgramCompiler extends Object
|
|||
hasUnnamedSuper = hasUnnamedSuper || _hasUnnamedConstructor(m.classNode);
|
||||
|
||||
if (shouldDefer(m)) {
|
||||
deferredSupertypes.add(runtimeStatement('mixinMembers(#, #)',
|
||||
deferredSupertypes.add(runtimeStatement('applyMixin(#, #)',
|
||||
[getBaseClass(mixins.length - i), emitDeferredType(m)]));
|
||||
} else {
|
||||
body.add(
|
||||
runtimeStatement('mixinMembers(#, #)', [mixinId, emitClassRef(m)]));
|
||||
runtimeStatement('applyMixin(#, #)', [mixinId, emitClassRef(m)]));
|
||||
}
|
||||
|
||||
baseClass = mixinId;
|
||||
}
|
||||
|
||||
body.add(_emitClassStatement(c, className, baseClass, methods));
|
||||
if (isMixinDeclaration(c)) {
|
||||
_emitMixinStatement(c, className, baseClass, methods, body);
|
||||
} else {
|
||||
body.add(_emitClassStatement(c, className, baseClass, methods));
|
||||
}
|
||||
|
||||
_classEmittingExtends = savedTopLevelClass;
|
||||
}
|
||||
|
||||
|
|
|
@ -234,3 +234,6 @@ Class getSuperclassAndMixins(Class c, List<Class> mixins) {
|
|||
}
|
||||
return sc;
|
||||
}
|
||||
|
||||
// TODO(jmesserly): replace with a flag on Class once Kernel supports it.
|
||||
bool isMixinDeclaration(Class c) => false;
|
||||
|
|
|
@ -12,6 +12,8 @@ import 'package:kernel/target/targets.dart';
|
|||
class DevCompilerTarget extends Target {
|
||||
bool get strongMode => true; // the only correct answer
|
||||
|
||||
bool get enableSuperMixins => true;
|
||||
|
||||
String get name => 'dartdevc';
|
||||
|
||||
List<String> get extraRequiredLibraries => const [
|
||||
|
|
|
@ -13,9 +13,7 @@
|
|||
part of dart._runtime;
|
||||
|
||||
/// Returns a new type that mixes members from base and the mixin.
|
||||
///
|
||||
/// The mixin must be non-generic; generic mixins are handled by [genericMixin].
|
||||
void mixinMembers(to, from) {
|
||||
void applyMixin(to, from) {
|
||||
JS('', '#[#] = #', to, _mixin, from);
|
||||
var toProto = JS('', '#.prototype', to);
|
||||
var fromProto = JS('', '#.prototype', from);
|
||||
|
@ -24,6 +22,11 @@ void mixinMembers(to, from) {
|
|||
_mixinSignature(to, from, _fieldSig);
|
||||
_mixinSignature(to, from, _getterSig);
|
||||
_mixinSignature(to, from, _setterSig);
|
||||
var mixinOnFn = JS('', '#[#]', from, mixinOn);
|
||||
if (mixinOnFn != null) {
|
||||
var proto = JS('', '#(#.__proto__).prototype', mixinOnFn, to);
|
||||
_copyMembers(toProto, proto);
|
||||
}
|
||||
}
|
||||
|
||||
void _copyMembers(to, from) {
|
||||
|
@ -97,16 +100,18 @@ final _mixin = JS('', 'Symbol("mixin")');
|
|||
getMixin(clazz) => JS('', 'Object.hasOwnProperty.call(#, #) ? #[#] : null',
|
||||
clazz, _mixin, clazz, _mixin);
|
||||
|
||||
final mixinOn = JS('', 'Symbol("mixinOn")');
|
||||
|
||||
@JSExportName('implements')
|
||||
final _implements = JS('', 'Symbol("implements")');
|
||||
final implements_ = JS('', 'Symbol("implements")');
|
||||
|
||||
List Function() getImplements(clazz) => JS(
|
||||
'',
|
||||
'Object.hasOwnProperty.call(#, #) ? #[#] : null',
|
||||
clazz,
|
||||
_implements,
|
||||
implements_,
|
||||
clazz,
|
||||
_implements);
|
||||
implements_);
|
||||
|
||||
/// The Symbol for storing type arguments on a specialized generic type.
|
||||
final _typeArguments = JS('', 'Symbol("typeArguments")');
|
||||
|
|
|
@ -620,6 +620,30 @@ class Expect {
|
|||
"on ${Error.safeToString(object)}");
|
||||
}
|
||||
|
||||
/// Checks that `Sub` is a subtype of `Super` at compile time and run time.
|
||||
static bool subtype<Sub extends Super, Super>() {
|
||||
List<Super> list = <Sub>[];
|
||||
_subtypeAtRuntime<Sub, Super>();
|
||||
}
|
||||
|
||||
/// Checks that `Sub` is a subtype of `Super` at run time.
|
||||
///
|
||||
/// This is similar to [subtype] but without the `Sub extends Super` generic
|
||||
/// constraint, so a compiler is less likely to optimize away the `is` check
|
||||
/// because the types appear to be unrelated.
|
||||
static bool _subtypeAtRuntime<Sub, Super>() {
|
||||
if (<Sub>[] is! List<Super>) {
|
||||
fail("$Sub is not a subtype of $Super");
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that `Sub` is not a subtype of `Super` at run time.
|
||||
static bool notSubtype<Sub, Super>() {
|
||||
if (<Sub>[] is List<Super>) {
|
||||
fail("$Sub is a subtype of $Super");
|
||||
}
|
||||
}
|
||||
|
||||
static String _getMessage(String reason) =>
|
||||
(reason == null) ? "" : ", '$reason'";
|
||||
|
||||
|
|
|
@ -233,7 +233,6 @@ void/*: Skip # https://github.com/dart-lang/sdk/issues/34013
|
|||
|
||||
[ $compiler == dartdevc || $compiler == dartdevk ]
|
||||
double_literals/*: Skip # https://github.com/dart-lang/sdk/issues/34359
|
||||
mixin_declaration/*: Skip # See https://github.com/dart-lang/language/issues/7
|
||||
|
||||
[ $hot_reload || $hot_reload_rollback ]
|
||||
cha_deopt1_test: Crash # Requires deferred libraries
|
||||
|
|
|
@ -72,10 +72,9 @@ issue31596_super_test/none: CompileTimeError # Issue #31596
|
|||
issue31596_tearoff_test: CompileTimeError # Issue #31596
|
||||
issue31596_test: CompileTimeError # Issue #31596
|
||||
malformed2_test: Pass, MissingCompileTimeError # Flaky: issue 31056.
|
||||
mixin_declaration/mixin_declaration_invalid_superinvocation_test/none: CompileTimeError # bug in analyzer? Exchange UnaryNum, UnaryOptionalNum, and it works.
|
||||
mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError # bug in analyzer? Exchange UnaryNum, UnaryOptionalNum, and it works.
|
||||
mixin_declaration/mixin_declaration_superinvocation_application_test/05: MissingCompileTimeError
|
||||
mixin_declaration/mixin_declaration_supertype_compatible_test/04: MissingCompileTimeError # no most specific signature
|
||||
mixin_declaration/mixin_declaration_syntax_test: CompileTimeError # test issue: many of them
|
||||
mixin_forwarding_constructor4_test/01: CompileTimeError # See issue #34375
|
||||
mixin_forwarding_constructor4_test/02: CompileTimeError # See issue #34375
|
||||
mixin_forwarding_constructor4_test/03: CompileTimeError # See issue #34375
|
||||
|
|
|
@ -104,6 +104,9 @@ issue31596_test: CompileTimeError
|
|||
issue32353_test: RuntimeError
|
||||
label_test: RuntimeError
|
||||
left_shift_test: RuntimeError # Ints and doubles are unified.
|
||||
mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError # Analyzer chooses wrong(?) super method.
|
||||
mixin_declaration/mixin_declaration_superinvocation_application_test/05: MissingCompileTimeError
|
||||
mixin_declaration/mixin_declaration_supertype_compatible_test/04: MissingCompileTimeError
|
||||
mixin_forwarding_constructor4_test/01: CompileTimeError # See issue 15101
|
||||
mixin_forwarding_constructor4_test/02: CompileTimeError # See issue 15101
|
||||
mixin_forwarding_constructor4_test/03: CompileTimeError # See issue 15101
|
||||
|
@ -290,6 +293,7 @@ issue31596_super_test/05: RuntimeError
|
|||
map_literal3_test/01: MissingCompileTimeError
|
||||
map_literal3_test/02: MissingCompileTimeError
|
||||
map_literal3_test/03: MissingCompileTimeError
|
||||
mixin_declaration/*: Skip # need flag on Kernel Class nodes.
|
||||
mixin_illegal_super_use_test/01: MissingCompileTimeError
|
||||
mixin_illegal_super_use_test/04: MissingCompileTimeError
|
||||
mixin_illegal_super_use_test/07: MissingCompileTimeError
|
||||
|
|
|
@ -89,11 +89,10 @@ mixin_declaration/mixin_declaration_invalid_application_supertype_test/03: Missi
|
|||
mixin_declaration/mixin_declaration_invalid_application_supertype_test/04: MissingCompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_application_supertype_test/05: MissingCompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_override_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_superinvocation_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_syntax_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_usage_test/03: MissingCompileTimeError
|
||||
mixin_declaration/mixin_declaration_nsm_test: RuntimeError
|
||||
mixin_declaration/mixin_declaration_subtype_test: RuntimeError
|
||||
mixin_declaration/mixin_declaration_superinvocation_application_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_supertype_compatible_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_syntax_test: CompileTimeError
|
||||
|
@ -214,6 +213,7 @@ mixin_declaration/mixin_declaration_invalid_application_supertype_test/03: Missi
|
|||
mixin_declaration/mixin_declaration_invalid_application_supertype_test/04: MissingCompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_application_supertype_test/05: MissingCompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_override_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_superinvocation_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_syntax_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_usage_test/03: MissingCompileTimeError
|
||||
|
@ -2137,11 +2137,10 @@ mixin_declaration/mixin_declaration_invalid_application_supertype_test/03: Missi
|
|||
mixin_declaration/mixin_declaration_invalid_application_supertype_test/04: MissingCompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_application_supertype_test/05: MissingCompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_override_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_superinvocation_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_syntax_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_invalid_usage_test/03: MissingCompileTimeError
|
||||
mixin_declaration/mixin_declaration_nsm_test: RuntimeError
|
||||
mixin_declaration/mixin_declaration_subtype_test: RuntimeError
|
||||
mixin_declaration/mixin_declaration_superinvocation_application_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_supertype_compatible_test/none: CompileTimeError
|
||||
mixin_declaration/mixin_declaration_syntax_test: CompileTimeError
|
||||
|
|
|
@ -41,7 +41,7 @@ mixin M2 on UnaryNum, UnaryInt {
|
|||
mixin M3 on UnaryNum, UnaryOptionalNum {
|
||||
void bar() {
|
||||
super.foo(4.2);
|
||||
super.foo();
|
||||
super.foo(); //# 10: ok
|
||||
super.foo(1, 2); //# 08: compile-time error
|
||||
super.foo("not num"); //# 09: compile-time error
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@ abstract class Foo {
|
|||
}
|
||||
|
||||
mixin M implements Bar {
|
||||
dynamic noSuchMethod(i) => "M${i.memberName == #foo ? "foo" : "bar"}";
|
||||
dynamic noSuchMethod(i) => "M:${i.memberName == #foo ? "foo" : "bar"}";
|
||||
}
|
||||
|
||||
abstract class C {
|
||||
dynamic noSuchMethod(i) => "C${i.memberName == #foo ? "foo" : "bar"}";
|
||||
dynamic noSuchMethod(i) => "C:${i.memberName == #foo ? "foo" : "bar"}";
|
||||
}
|
||||
|
||||
abstract class D {
|
||||
|
|
|
@ -30,34 +30,22 @@ class GC<T> implements GA<T>, GB<List<T>> {}
|
|||
|
||||
class GD<T> = GC<T> with GM<T>;
|
||||
|
||||
bool expectSubtype<Sub, Super>() {
|
||||
if (<Sub>[] is! List<Super>) {
|
||||
Expect.fail("$Sub is not a subtype of $Super");
|
||||
}
|
||||
}
|
||||
|
||||
bool expectNotSubtype<Sub, Super>() {
|
||||
if (<Sub>[] is List<Super>) {
|
||||
Expect.fail("$Sub is a subtype of $Super");
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
expectSubtype<M, A>();
|
||||
expectSubtype<M, B>();
|
||||
expectSubtype<M, I>();
|
||||
expectSubtype<M, J>();
|
||||
expectSubtype<D, M>();
|
||||
expectSubtype<D, C>();
|
||||
expectNotSubtype<M, C>();
|
||||
expectNotSubtype<C, M>();
|
||||
Expect.subtype<M, A>();
|
||||
Expect.subtype<M, B>();
|
||||
Expect.subtype<M, I>();
|
||||
Expect.subtype<M, J>();
|
||||
Expect.subtype<D, M>();
|
||||
Expect.subtype<D, C>();
|
||||
Expect.notSubtype<M, C>();
|
||||
Expect.notSubtype<C, M>();
|
||||
|
||||
expectSubtype<GM<int>, GA<int>>();
|
||||
expectSubtype<GM<int>, GB<int>>();
|
||||
expectSubtype<GM<int>, GI<int>>();
|
||||
expectSubtype<GM<int>, GJ<int>>();
|
||||
expectSubtype<GD<int>, GM<int>>();
|
||||
expectSubtype<GD<int>, GC<int>>();
|
||||
expectNotSubtype<GM<int>, GC<int>>();
|
||||
expectNotSubtype<GC<int>, GM<int>>();
|
||||
Expect.subtype<GM<int>, GA<int>>();
|
||||
Expect.subtype<GM<int>, GB<List<int>>>();
|
||||
Expect.subtype<GM<int>, GI<Iterable<int>>>();
|
||||
Expect.subtype<GM<int>, GJ<Set<int>>>();
|
||||
Expect.subtype<GD<int>, GM<int>>();
|
||||
Expect.subtype<GD<int>, GC<int>>();
|
||||
Expect.notSubtype<GM<int>, GC<int>>();
|
||||
Expect.notSubtype<GC<int>, GM<int>>();
|
||||
}
|
|
@ -91,6 +91,14 @@ mixin MAiBC on A implements B, C {
|
|||
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 {
|
||||
|
@ -104,11 +112,11 @@ mixin MBC on B, C {
|
|||
|
||||
// One with everything.
|
||||
mixin MBCiIJ on B, C implements I, J {
|
||||
String toString() => "${super.toString()}&MBC";
|
||||
String toString() => "${super.toString()}&MBCiIJ";
|
||||
String methodMBCiIJ() => "MBCiIJ:${this}.MBCiIJ";
|
||||
String methodA() => "MBC:${this}.A->${super.methodA()}";
|
||||
String methodB() => "MBC:${this}.B->${super.methodB()}";
|
||||
String methodC() => "MBC:${this}.C->${super.methodC()}";
|
||||
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";
|
||||
}
|
||||
|
@ -116,7 +124,7 @@ mixin MBCiIJ on B, C implements I, J {
|
|||
|
||||
// Abstract mixin, doesn't implement its interface.
|
||||
mixin MiIJ implements I, J {
|
||||
String toString = "${super.toString}&MiIJ";
|
||||
String toString() => "${super.toString()}&MiIJ";
|
||||
}
|
||||
|
||||
|
||||
|
@ -142,9 +150,9 @@ class COaMOiIJ = O with MOiIJ;
|
|||
|
||||
class COaMOiIJ_2 extends O with MOiIJ {}
|
||||
|
||||
class CBaMOiIJ = B with MBiIJ;
|
||||
class CBaMBiIJ = B with MBiIJ;
|
||||
|
||||
class CBaMOiIJ_2 extends B with MBiIJ {}
|
||||
class CBaMBiIJ_2 extends B with MBiIJ {}
|
||||
|
||||
class CAaMA = A with MA;
|
||||
|
||||
|
@ -154,13 +162,13 @@ class CBaMA = B with MA;
|
|||
|
||||
class CBaMA_2 extends B with MA {}
|
||||
|
||||
class CAaMAiBC = A with CAaMAiBC;
|
||||
class CAaMAiBC = A with MAiBC;
|
||||
|
||||
class CAaMAiBC_2 extends A with CAaMAiBC {}
|
||||
class CAaMAiBC_2 extends A with MAiBC {}
|
||||
|
||||
class CBaMAiBC = B with CAaMAiBC;
|
||||
class CBaMAiBC = B with MAiBC;
|
||||
|
||||
class CBaMAiBC_2 extends B with CAaMAiBC {}
|
||||
class CBaMAiBC_2 extends B with MAiBC {}
|
||||
|
||||
class CBCaMBC = BC with MBC;
|
||||
|
||||
|
@ -182,7 +190,17 @@ class CAaMAiBCaMBCiIJ_2 extends CAaMAiBC with MBCiIJ {}
|
|||
abstract class OaMiIJ = O with MiIJ;
|
||||
|
||||
// Concrete subclass of abstract mixin appliction
|
||||
class COaMiIJ extends OaMIJ {
|
||||
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";
|
||||
|
@ -195,7 +213,7 @@ class CeOwithM extends Object with M {}
|
|||
// Test that the mixin applications behave as expected.
|
||||
void main() {
|
||||
{
|
||||
for (var o in [COaM(), COaM_2()]) {
|
||||
for (dynamic o in [COaM(), COaM_2()]) {
|
||||
Expect.type<O>(o);
|
||||
Expect.type<M>(o);
|
||||
Expect.equals("?&M", "$o");
|
||||
|
@ -207,14 +225,14 @@ void main() {
|
|||
for (var o in [CBaM(), CBaM_2()]) {
|
||||
Expect.type<B>(o);
|
||||
Expect.type<M>(o);
|
||||
Expect.equals("&M", "$o");
|
||||
Expect.equals("?&M", "$o");
|
||||
Expect.equals("B:$o.B", o.methodB());
|
||||
Expect.equals("M:$o.M", o.methodM());
|
||||
Expect.equals("M:$o.M", (o as M).methodM());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (var o in [COaMO(), COaMO_2()]) {
|
||||
for (dynamic o in [COaMO(), COaMO_2()]) {
|
||||
Expect.type<O>(o);
|
||||
Expect.type<MO>(o);
|
||||
Expect.equals("O&MO", "$o");
|
||||
|
@ -227,13 +245,13 @@ void main() {
|
|||
Expect.type<B>(o);
|
||||
Expect.type<MO>(o);
|
||||
Expect.equals("B&MO", "$o");
|
||||
Expect.equals("MO:$o.MO", o.methodMO());
|
||||
Expect.equals("MO:$o.MO", (o as MO).methodMO());
|
||||
Expect.equals("B:$o.B", o.methodB());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (var o in [COaMOiIJ(), COaMOiIJ_2()]) {
|
||||
for (dynamic o in [COaMOiIJ(), COaMOiIJ_2()]) {
|
||||
Expect.type<O>(o);
|
||||
Expect.type<I>(o);
|
||||
Expect.type<J>(o);
|
||||
|
@ -246,21 +264,21 @@ void main() {
|
|||
}
|
||||
|
||||
{
|
||||
for (var o in [CBaMOiIJ(), CBaMOiIJ_2()]) {
|
||||
for (dynamic o in [CBaMBiIJ(), CBaMBiIJ_2()]) {
|
||||
Expect.type<B>(o);
|
||||
Expect.type<I>(o);
|
||||
Expect.type<J>(o);
|
||||
Expect.type<MOiIJ>(o);
|
||||
Expect.equals("B&MOiIJ", "$o");
|
||||
Expect.equals("MOiIJ:$o.MOiIJ", o.methodMOiIJ());
|
||||
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("MOiIJ:$o.I", o.methodI());
|
||||
Expect.equals("MOiIJ:$o.J", o.methodJ());
|
||||
Expect.equals("MBiIJ:$o.I", o.methodI());
|
||||
Expect.equals("MBiIJ:$o.J", o.methodJ());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (var o in [CAaMA(), CAaMA_2()]) {
|
||||
for (dynamic o in [CAaMA(), CAaMA_2()]) {
|
||||
Expect.type<A>(o);
|
||||
Expect.type<MA>(o);
|
||||
Expect.equals("A&MA", "$o");
|
||||
|
@ -273,20 +291,20 @@ void main() {
|
|||
for (var o in [CBaMA(), CBaMA_2()]) {
|
||||
Expect.type<B>(o);
|
||||
Expect.type<MA>(o);
|
||||
Expect.equals("A&MA", "$o");
|
||||
Expect.equals("MA:$o.MA", o.methodMA());
|
||||
Expect.equals("MA:$o.A->B:$o.A", o.methodA());
|
||||
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());
|
||||
Expect.equals("B:$o.B", o.methodB());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (var o in [CAaMAiBC(), CAaMAiBC_2()]) {
|
||||
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&MA", "$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());
|
||||
|
@ -295,21 +313,21 @@ void main() {
|
|||
}
|
||||
|
||||
{
|
||||
for (var o in [CBaMAiBC(), CBaMAiBC_2()]) {
|
||||
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&MA", "$o");
|
||||
Expect.equals("B&MAiBC", "$o");
|
||||
Expect.equals("MAiBC:$o.MAiBC", o.methodMAiBC());
|
||||
Expect.equals("MA:$o.A->B:$o.A", o.methodA());
|
||||
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 (var o in [CBCaMBC(), CBCaMBC_2()]) {
|
||||
for (dynamic o in [CBCaMBC(), CBCaMBC_2()]) {
|
||||
Expect.type<BC>(o);
|
||||
Expect.type<MBC>(o);
|
||||
Expect.equals("BC&MBC", "$o");
|
||||
|
@ -322,11 +340,11 @@ void main() {
|
|||
|
||||
{
|
||||
// Mixin on top of mixin application.
|
||||
for (var o in [CAaMAiBCaMBC(), CAaMAiBCaMBC_2()]) {
|
||||
for (dynamic o in [CAaMAiBCaMBC(), CAaMAiBCaMBC_2()]) {
|
||||
Expect.type<CAaMAiBC>(o);
|
||||
Expect.type<MBC>(o);
|
||||
Expect.equals("CA&MAiBC&MBC", "$o");
|
||||
Expect.equals("MBC:$o.MBC", o.methodMBC());
|
||||
Expect.equals("A&MAiBC&MBC", "$o");
|
||||
Expect.equals("MBC:$o.MBC", (o as MBC).methodMBC());
|
||||
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());
|
||||
|
@ -335,7 +353,7 @@ void main() {
|
|||
}
|
||||
|
||||
{
|
||||
for (var o in [CBCaMBCiIJ(), CBCaMBCiIJ_2()]) {
|
||||
for (dynamic o in [CBCaMBCiIJ(), CBCaMBCiIJ_2()]) {
|
||||
Expect.type<BC>(o);
|
||||
Expect.type<MBCiIJ>(o);
|
||||
Expect.type<I>(o);
|
||||
|
@ -352,14 +370,14 @@ void main() {
|
|||
|
||||
{
|
||||
// Mixin on top of mixin application.
|
||||
for (var o in [CAaMAiBCaMBCiIJ(), CAaMAiBCaMBCiIJ_2()]) {
|
||||
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("CA&MAiBC&MBCiIJ", "$o");
|
||||
Expect.equals("A&MAiBC&MBCiIJ", "$o");
|
||||
Expect.equals("MBCiIJ:$o.MBCiIJ", o.methodMBCiIJ());
|
||||
Expect.equals("MAiBC:$o.MAiBC", o.methodMAiBC());
|
||||
Expect.equals("MAiBC:$o.MAiBC", (o as CAaMAiBC).methodMAiBC());
|
||||
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());
|
||||
|
@ -370,13 +388,14 @@ void main() {
|
|||
|
||||
{
|
||||
// Abstract mixin application, concrete subclass.
|
||||
for (var o in [COaMiIJ(), COaMiIJ_2()]) {
|
||||
for (dynamic o in [COaMiIJ(), COaMiIJ_2()]) {
|
||||
Expect.type<O>(o);
|
||||
Expect.type<MiIJ>(o);
|
||||
Expect.type<OaMiIJ>(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");
|
||||
Expect.equals("O&MiIJ:COaMiIJ", "$o");
|
||||
Expect.equals("COaMiIJ:$o.I", o.methodI());
|
||||
Expect.equals("COaMiIJ:$o.J", o.methodJ());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue