Support for modular mixin resolution.

When a class from a different build unit is mixed in, the instance
members of the mixed-in class are retained in the external library
definition, so the mixin resolution pass can clone them.

BUG=
R=kmillikin@google.com

Review-Url: https://codereview.chromium.org/2669303002 .
diff --git a/pkg/kernel/bin/dartk.dart b/pkg/kernel/bin/dartk.dart
index 00ef986..84fbf77 100755
--- a/pkg/kernel/bin/dartk.dart
+++ b/pkg/kernel/bin/dartk.dart
@@ -340,6 +340,7 @@
       program = loader.loadProgram(fileUri, target: target);
     } else {
       var library = loader.loadLibrary(fileUri);
+      loader.loadSdkInterface(program, target);
       assert(library ==
           repository.getLibraryReference(applicationRoot.relativeUri(fileUri)));
       program = new Program(repository.libraries);
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index a287dd6..dfeb53e 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -184,6 +184,8 @@
   Byte tag;
 }
 
+enum ClassLevel { Type = 0, Hierarchy = 1, Mixin = 2, Body = 3, }
+
 // A class can be represented at one of three levels: type, hierarchy, or body.
 //
 // If the enclosing library is external, a class is either at type or
@@ -197,7 +199,7 @@
 type NormalClass extends Class {
   Byte tag = 2;
   FileOffset fileOffset;
-  Byte flags (isAbstract, isTypeLevel);
+  Byte flags (isAbstract, xx); // Where xx is index into ClassLevel
   StringReference name;
   // An absolute path URI to the .dart file from which the class was created.
   UriReference fileUri;
diff --git a/pkg/kernel/lib/analyzer/loader.dart b/pkg/kernel/lib/analyzer/loader.dart
index 664bf79..d9dbf13 100644
--- a/pkg/kernel/lib/analyzer/loader.dart
+++ b/pkg/kernel/lib/analyzer/loader.dart
@@ -106,6 +106,8 @@
   /// so as not to expose partially initialized classes.
   final List<ast.Class> temporaryClassWorklist = [];
 
+  final Map<LibraryElement, List<ClassElement>> mixinLibraryWorklist = {};
+
   final bool ignoreRedirectingFactories;
 
   LibraryElement _libraryBeingLoaded = null;
@@ -113,8 +115,9 @@
   bool get strongMode => context.analysisOptions.strongMode;
 
   DartLoader(this.repository, DartOptions options, Packages packages,
-      {DartSdk dartSdk, AnalysisContext context,
-       this.ignoreRedirectingFactories: true})
+      {DartSdk dartSdk,
+      AnalysisContext context,
+      this.ignoreRedirectingFactories: true})
       : this.context =
             context ?? createContext(options, packages, dartSdk: dartSdk),
         this.applicationRoot = options.applicationRoot;
@@ -199,7 +202,7 @@
       }
     }
     libraryElements.add(element);
-    _iterateWorklist();
+    _iterateTemporaryClassWorklist();
     // Ensure everything is stored in the original declaration order.
     library.classes
       ..clear()
@@ -388,15 +391,43 @@
     }
   }
 
-  void promoteToBodyLevel(ast.Class classNode, ClassElement element,
+  void promoteToMixinLevel(ast.Class classNode, ClassElement element,
       NamedCompilationUnitMember astNode) {
-    if (classNode.level == ast.ClassLevel.Body) return;
+    if (classNode.level.index >= ast.ClassLevel.Mixin.index) return;
     promoteToHierarchyLevel(classNode);
-    classNode.level = ast.ClassLevel.Body;
+    classNode.level = ast.ClassLevel.Mixin;
     // Clear out the member references that were put in the class.
     // The AST builder will load them all put back in the right order.
     classNode..fields.clear()..procedures.clear()..constructors.clear();
     new ClassBodyBuilder(this, classNode, element).build(astNode);
+
+    // Ensure mixed-in classes are available.
+    for (var mixin in element.mixins) {
+      _ensureMixinBecomesLoaded(mixin.element);
+    }
+  }
+
+  /// Ensures that [element] eventually becomes loaded at least at mixin level.
+  void _ensureMixinBecomesLoaded(ClassElement element) {
+    if (isLibraryBeingLoaded(element.library)) {
+      return;
+    }
+    var class_ = getClassReference(element);
+    if (class_.level.index >= ast.ClassLevel.Mixin.index) {
+      return;
+    }
+    var list = mixinLibraryWorklist[element.library] ??= <ClassElement>[];
+    list.add(element);
+  }
+
+  void promoteToBodyLevel(ast.Class classNode, ClassElement element,
+      NamedCompilationUnitMember astNode) {
+    if (classNode.level == ast.ClassLevel.Body) return;
+    promoteToMixinLevel(classNode, element, astNode);
+    classNode.level = ast.ClassLevel.Body;
+    // This frontend delivers the same contents for classes at body and mixin
+    // levels, even though as specified, the mixin level does not require all
+    // the static members to be present.  So no additional work is needed.
   }
 
   ast.TypeParameter tryGetClassTypeParameter(TypeParameterElement element) {
@@ -617,6 +648,11 @@
   }
 
   void ensureLibraryIsLoaded(ast.Library node) {
+    _ensureLibraryIsLoaded(node);
+    _iterateMixinLibraryWorklist();
+  }
+
+  void _ensureLibraryIsLoaded(ast.Library node) {
     if (!node.isExternal) return;
     node.isExternal = false;
     var source = context.sourceFactory
@@ -649,6 +685,23 @@
     }
   }
 
+  void loadSdkInterface(ast.Program program, Target target) {
+    var requiredSdkMembers = target.requiredSdkClasses;
+    for (var libraryUri in requiredSdkMembers.keys) {
+      var source = context.sourceFactory.forUri2(Uri.parse(libraryUri));
+      var libraryElement = context.computeLibraryElement(source);
+      for (var member in requiredSdkMembers[libraryUri]) {
+        var type = libraryElement.getType(member);
+        if (type == null) {
+          throw 'Could not find $member in $libraryUri';
+        }
+        promoteToTypeLevel(getClassReference(type));
+      }
+    }
+    _iterateTemporaryClassWorklist();
+    _iterateMixinLibraryWorklist();
+  }
+
   void loadEverything({Target target, bool compileSdk}) {
     compileSdk ??= true;
     if (compileSdk) {
@@ -688,13 +741,29 @@
     return list;
   }
 
-  void _iterateWorklist() {
+  void _iterateTemporaryClassWorklist() {
     while (temporaryClassWorklist.isNotEmpty) {
       var element = temporaryClassWorklist.removeLast();
       promoteToTypeLevel(element);
     }
   }
 
+  void _iterateMixinLibraryWorklist() {
+    // The worklist groups classes in the same library together so that we
+    // request resolved ASTs for each library only once.
+    while (mixinLibraryWorklist.isNotEmpty) {
+      LibraryElement library = mixinLibraryWorklist.keys.first;
+      _libraryBeingLoaded = library;
+      List<ClassElement> classes = mixinLibraryWorklist.remove(library);
+      for (var class_ in classes) {
+        var classNode = getClassReference(class_);
+        promoteToMixinLevel(classNode, class_, class_.computeNode());
+      }
+      _libraryBeingLoaded = null;
+    }
+    _iterateTemporaryClassWorklist();
+  }
+
   ast.Procedure _getMainMethod(Uri uri) {
     Source source = context.sourceFactory.forUri2(uri);
     LibraryElement library = context.computeLibraryElement(source);
@@ -714,8 +783,7 @@
         ast.ProcedureKind.Method,
         new ast.FunctionNode(new ast.ExpressionStatement(new ast.Throw(
             new ast.StringLiteral('Program has no main method')))),
-        isStatic: true)
-      ..fileUri = library.fileUri;
+        isStatic: true)..fileUri = library.fileUri;
     library.addMember(main);
     return main;
   }
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 80a62f04..30998cd 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -275,6 +275,18 @@
   /// members needed to detect override constraints.
   Hierarchy,
 
+  /// All instance members of the class have their body loaded, and their
+  /// annotations are present.
+  ///
+  /// All supertypes of this class are at [Hierarchy] level or higher.
+  ///
+  /// If this class is a mixin application, then its mixin is loaded at [Mixin]
+  /// level or higher.
+  ///
+  /// This level exists so the contents of a mixin can be cloned into a
+  /// mixin application.
+  Mixin,
+
   /// All members of the class are fully loaded and are in the correct order.
   ///
   /// Annotations are present on classes and members.
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 8c940f6..0ca2f607 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -324,9 +324,8 @@
     node.fileOffset = readOffset();
     int flags = readByte();
     node.isAbstract = flags & 0x1 != 0;
-    node.level = _currentLibrary.isExternal
-        ? (flags & 0x2 != 0) ? ClassLevel.Type : ClassLevel.Hierarchy
-        : ClassLevel.Body;
+    int levelIndex = (flags >> 1) & 0x3;
+    node.level = ClassLevel.values[levelIndex + 1];
     node.name = readStringOrNullIfEmpty();
     node.fileUri = readUriReference();
     node.annotations = readAnnotationList(node);
@@ -351,9 +350,8 @@
     node.fileOffset = readOffset();
     int flags = readByte();
     node.isAbstract = flags & 0x1 != 0;
-    node.level = _currentLibrary.isExternal
-        ? (flags & 0x2 != 0) ? ClassLevel.Type : ClassLevel.Hierarchy
-        : ClassLevel.Body;
+    int levelIndex = (flags >> 1) & 0x3;
+    node.level = ClassLevel.values[levelIndex];
     node.name = readStringOrNullIfEmpty();
     node.fileUri = readUriReference();
     node.annotations = readAnnotationList(node);
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 6d3dabd..b838d5e 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -283,11 +283,14 @@
     writeList(annotations, writeAnnotation);
   }
 
+  int _encodeClassFlags(bool isAbstract, ClassLevel level) {
+    int abstactFlag = isAbstract ? 1 : 0;
+    int levelFlags = (level.index - 1) << 1;
+    return abstactFlag | levelFlags;
+  }
+
   visitClass(Class node) {
-    int flags = node.isAbstract ? 1 : 0;
-    if (node.level == ClassLevel.Type) {
-      flags |= 0x2;
-    }
+    int flags = _encodeClassFlags(node.isAbstract, node.level);
     if (node.isMixinApplication) {
       writeByte(Tag.MixinClass);
       writeOffset(node, node.fileOffset);
diff --git a/pkg/kernel/lib/core_types.dart b/pkg/kernel/lib/core_types.dart
index 28ce858e..72bf93d9 100644
--- a/pkg/kernel/lib/core_types.dart
+++ b/pkg/kernel/lib/core_types.dart
@@ -27,6 +27,33 @@
   Class functionClass;
   Class invocationClass;
 
+  static final Map<String, List<String>> requiredClasses = {
+    'dart:core': [
+      'Object',
+      'Null',
+      'bool',
+      'int',
+      'num',
+      'double',
+      'String',
+      'List',
+      'Map',
+      'Iterable',
+      'Iterator',
+      'Symbol',
+      'Type',
+      'Function',
+      'Invocation',
+    ],
+    'dart:_internal': [
+      'Symbol',
+    ],
+    'dart:async': [
+      'Future',
+      'Stream',
+    ]
+  };
+
   Library getCoreLibrary(String uri) => _dartLibraries[uri]?.library;
 
   Class getCoreClass(String libraryUri, String className) {
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index e1d6a21..ec89ffb 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -4,11 +4,11 @@
 library kernel.target.targets;
 
 import '../ast.dart';
-
+import '../core_types.dart';
+import '../transformations/treeshaker.dart' show ProgramRoot;
+import 'flutter.dart';
 import 'vm.dart';
 import 'vmcc.dart';
-import 'flutter.dart';
-import '../transformations/treeshaker.dart' show ProgramRoot;
 
 final List<String> targetNames = targets.keys.toList();
 
@@ -51,6 +51,10 @@
   /// by the target.
   Map<String, String> get extraDeclaredVariables => const <String, String>{};
 
+  /// Classes from the SDK whose interface is required for the modular
+  /// transformations.
+  Map<String, List<String>> get requiredSdkClasses => CoreTypes.requiredClasses;
+
   bool get strongMode;
 
   /// If true, the SDK should be loaded in strong mode.
diff --git a/pkg/kernel/lib/transformations/mixin_full_resolution.dart b/pkg/kernel/lib/transformations/mixin_full_resolution.dart
index 92ed3b7..8d84347 100644
--- a/pkg/kernel/lib/transformations/mixin_full_resolution.dart
+++ b/pkg/kernel/lib/transformations/mixin_full_resolution.dart
@@ -78,7 +78,8 @@
     if (!processedClasses.add(class_)) return;
 
     // Ensure super classes have been transformed before this class.
-    if (class_.superclass != null) {
+    if (class_.superclass != null &&
+        class_.superclass.level.index >= ClassLevel.Mixin.index) {
       transformClass(processedClasses, transformedClasses, class_.superclass);
     }
 
@@ -86,7 +87,7 @@
     // constructors in this class.
     if (!class_.isMixinApplication) return;
 
-    if (class_.mixedInClass.level != ClassLevel.Body) {
+    if (class_.mixedInClass.level.index < ClassLevel.Mixin.index) {
       throw new Exception(
           'Class "${class_.name}" mixes in "${class_.mixedInClass.name}" from'
           ' an external library.  Did you forget --link?');