Add IR nodes needed for deferred loading.

BUG=
R=sra@google.com

Review-Url: https://codereview.chromium.org/2659343002 .
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index dfeb53e..6059690 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -175,11 +175,22 @@
   StringReference importUri;
   // An absolute path URI to the .dart file from which the library was created.
   UriReference fileUri;
+  List<DeferredImport> deferredImports;
   List<Class> classes;
   List<Field> fields;
   List<Procedure> procedures;
 }
 
+type DeferredImport {
+  LibraryReference importedLibrary;
+  StringReference name;
+}
+
+type DeferredImportReference {
+  // Index into deferredImports in the enclosing Library.
+  UInt index;
+}
+
 abstract type Node {
   Byte tag;
 }
@@ -666,6 +677,16 @@
   Expression body;
 }
 
+type LoadLibrary extends Expression {
+  Byte tag = 14;
+  DeferredImportReference import;
+}
+
+type CheckLibraryIsLoaded extends Expression {
+  Byte tag = 13;
+  DeferredImportReference import;
+}
+
 abstract type Statement extends Node {}
 
 type InvalidStatement extends Statement {
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 30998cd..2f3cb11 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -179,6 +179,7 @@
   bool isExternal;
 
   String name;
+  final List<DeferredImport> deferredImports;
   final List<Class> classes;
   final List<Procedure> procedures;
   final List<Field> fields;
@@ -186,11 +187,13 @@
   Library(this.importUri,
       {this.name,
       this.isExternal: false,
+      List<DeferredImport> imports,
       List<Class> classes,
       List<Procedure> procedures,
       List<Field> fields,
       this.fileUri})
-      : this.classes = classes ?? <Class>[],
+      : this.deferredImports = imports ?? <DeferredImport>[],
+        this.classes = classes ?? <Class>[],
         this.procedures = procedures ?? <Procedure>[],
         this.fields = fields ?? <Field>[] {
     setParents(this.classes, this);
@@ -249,6 +252,22 @@
   }
 }
 
+/// An import of form: `import <url> deferred as <name>;`.
+class DeferredImport extends TreeNode {
+  Library importedLibrary;
+  String name;
+
+  DeferredImport(this.importedLibrary, this.name);
+
+  Library get enclosingLibrary => parent;
+
+  accept(TreeVisitor v) => v.visitDeferredImport(this);
+
+  visitChildren(Visitor v) {}
+
+  transformChildren(Transformer v) {}
+}
+
 /// The degree to which the contents of a class have been loaded into memory.
 ///
 /// Each level imply the requirements of the previous ones.
@@ -2449,6 +2468,51 @@
   }
 }
 
+/// Attempt to load the library referred to by a deferred import.
+///
+/// This instruction is concerned with:
+/// - keeping track whether the deferred import is marked as 'loaded'
+/// - keeping track of whether the library code has already been downloaded
+/// - actually downloading and linking the library
+///
+/// Should return a future.  The value in this future will be the same value
+/// seen by callers of `loadLibrary` functions.
+///
+/// On backends that link the entire program eagerly, this instruction needs
+/// to mark the deferred import as 'loaded' and return a future.
+class LoadLibrary extends Expression {
+  /// Reference to a deferred import in the enclosing library.
+  DeferredImport import;
+
+  LoadLibrary(this.import);
+
+  DartType getStaticType(TypeEnvironment types) {
+    return types.futureType(const DynamicType());
+  }
+
+  accept(ExpressionVisitor v) => v.visitLoadLibrary(this);
+
+  visitChildren(Visitor v) {}
+  transformChildren(Transformer v) {}
+}
+
+/// Checks that the given deferred import has been marked as 'loaded'.
+class CheckLibraryIsLoaded extends Expression {
+  /// Reference to a deferred import in the enclosing library.
+  DeferredImport import;
+
+  CheckLibraryIsLoaded(this.import);
+
+  DartType getStaticType(TypeEnvironment types) {
+    return types.objectType;
+  }
+
+  accept(ExpressionVisitor v) => v.visitCheckLibraryIsLoaded(this);
+
+  visitChildren(Visitor v) {}
+  transformChildren(Transformer v) {}
+}
+
 // ------------------------------------------------------------------------
 //                              STATEMENTS
 // ------------------------------------------------------------------------
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 0ca2f607..100fe98 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -187,8 +187,7 @@
       readLibrary();
     }
     var mainMethod = readMemberReference(allowNull: true);
-    return new Program(importTable, uriToSource)
-      ..mainMethod = mainMethod;
+    return new Program(importTable, uriToSource)..mainMethod = mainMethod;
   }
 
   Map<String, Source> readUriToSource() {
@@ -226,6 +225,11 @@
     return importTable[index];
   }
 
+  DeferredImport readDeferredImportReference() {
+    int index = readUInt();
+    return _currentLibrary.deferredImports[index];
+  }
+
   Class readClassReference({bool allowNull: false}) {
     int tag = readByte();
     if (tag == Tag.NullReference) {
@@ -292,6 +296,7 @@
     // TODO(jensj): We currently save (almost the same) uri twice.
     _currentLibrary.fileUri = readUriReference();
 
+    _readDeferredImports(_currentLibrary);
     _fillLazilyLoadedList(_currentLibrary.classes, (int tag, int index) {
       readClass(loader.getClassReference(_currentLibrary, tag, index), tag);
     });
@@ -306,6 +311,19 @@
     debugPath.removeLast();
   }
 
+  void _readDeferredImports(Library library) {
+    int count = readUInt();
+    library.deferredImports.length = count;
+    for (int i = 0; i < count; ++i) {
+      var importNode = _readDeferredImport();
+      library.deferredImports.add(importNode..parent = library);
+    }
+  }
+
+  DeferredImport _readDeferredImport() {
+    return new DeferredImport(readLibraryReference(), readStringReference());
+  }
+
   void readClass(Class node, int tag) {
     assert(node != null);
     switch (tag) {
@@ -528,6 +546,10 @@
         ? tagByte
         : (tagByte & Tag.SpecializedTagMask);
     switch (tag) {
+      case Tag.LoadLibrary:
+        return new LoadLibrary(readDeferredImportReference());
+      case Tag.CheckLibraryIsLoaded:
+        return new CheckLibraryIsLoaded(readDeferredImportReference());
       case Tag.InvalidExpression:
         return new InvalidExpression();
       case Tag.VariableGet:
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index b838d5e..a137d86 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -24,6 +24,7 @@
   final GlobalIndexer _globalIndexer;
   final StringIndexer _stringIndexer = new StringIndexer();
   final StringIndexer _sourceUriIndexer = new StringIndexer();
+  Map<DeferredImport, int> _deferredImportIndexer = <DeferredImport, int>{};
 
   final BufferedSink _sink;
 
@@ -175,6 +176,14 @@
     writeUInt30(index);
   }
 
+  void writeDeferredImportReference(DeferredImport node) {
+    int index = _deferredImportIndexer[node];
+    if (index == null) {
+      throw 'Reference to deferred import $node out of scope';
+    }
+    writeUInt30(index);
+  }
+
   void writeClassIndex(Class node) {
     writeUInt30(_globalIndexer[node]);
   }
@@ -269,11 +278,29 @@
     writeStringReference('${node.importUri}');
     // TODO(jensj): We save (almost) the same URI twice.
     writeUriReference(node.fileUri ?? '');
+    writeDeferredImports(node);
     writeNodeList(node.classes);
     writeNodeList(node.fields);
     writeNodeList(node.procedures);
   }
 
+  void writeDeferredImports(Library library) {
+    _deferredImportIndexer = library.deferredImports.isEmpty
+        ? const <DeferredImport, int>{}
+        : <DeferredImport, int>{};
+    writeUInt30(library.deferredImports.length);
+    for (int i = 0; i < library.deferredImports.length; ++i) {
+      var importNode = library.deferredImports[i];
+      _deferredImportIndexer[importNode] = i;
+      writeDeferredImport(importNode);
+    }
+  }
+
+  void writeDeferredImport(DeferredImport node) {
+    writeLibraryReference(node.importedLibrary);
+    writeStringReference(node.name);
+  }
+
   void writeAnnotation(Expression annotation) {
     _variableIndexer ??= new VariableIndexer();
     writeNode(annotation);
@@ -715,6 +742,16 @@
     --_variableIndexer.stackHeight;
   }
 
+  visitLoadLibrary(LoadLibrary node) {
+    writeByte(Tag.LoadLibrary);
+    writeDeferredImportReference(node.import);
+  }
+
+  visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
+    writeByte(Tag.CheckLibraryIsLoaded);
+    writeDeferredImportReference(node.import);
+  }
+
   writeStatementOrEmpty(Statement node) {
     if (node == null) {
       writeByte(Tag.EmptyStatement);
@@ -1101,6 +1138,10 @@
     node.visitChildren(this);
   }
 
+  visitDeferredImport(DeferredImport node) {
+    put(node.name);
+  }
+
   visitClass(Class node) {
     putOptional(node.name);
     node.visitChildren(this);
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index 226b190..336b360 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -20,6 +20,8 @@
   static const int RedirectingInitializer = 10;
   static const int LocalInitializer = 11;
 
+  static const int CheckLibraryIsLoaded = 13;
+  static const int LoadLibrary = 14;
   static const int DirectPropertyGet = 15;
   static const int DirectPropertySet = 16;
   static const int DirectMethodInvocation = 17;
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 0e69e83..8fa2c67 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -302,6 +302,9 @@
         endLine('import "$importPath" as $prefix;');
       }
     }
+    for (var import in library.deferredImports) {
+      import.accept(this);
+    }
     endLine();
     var inner = new Printer._inner(this, imports);
     library.classes.forEach(inner.writeNode);
@@ -989,6 +992,30 @@
     writeExpression(node.body);
   }
 
+  visitLoadLibrary(LoadLibrary node) {
+    writeWord('LoadLibrary');
+    writeSymbol('(');
+    writeWord(node.import.name);
+    writeSymbol(')');
+    state = WORD;
+  }
+
+  visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
+    writeWord('CheckLibraryIsLoaded');
+    writeSymbol('(');
+    writeWord(node.import.name);
+    writeSymbol(')');
+    state = WORD;
+  }
+
+  visitDeferredImport(DeferredImport node) {
+    write('import "');
+    write('${node.importedLibrary.importUri}');
+    write('" deferred as ');
+    write(node.name);
+    endLine(';');
+  }
+
   defaultExpression(Expression node) {
     writeWord('${node.runtimeType}');
   }
diff --git a/pkg/kernel/lib/type_checker.dart b/pkg/kernel/lib/type_checker.dart
index 45159b2..c6a8937 100644
--- a/pkg/kernel/lib/type_checker.dart
+++ b/pkg/kernel/lib/type_checker.dart
@@ -680,6 +680,16 @@
   }
 
   @override
+  DartType visitLoadLibrary(LoadLibrary node) {
+    return environment.futureType(const DynamicType());
+  }
+
+  @override
+  DartType visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
+    return environment.objectType;
+  }
+
+  @override
   visitAssertStatement(AssertStatement node) {
     visitExpression(node.condition);
     if (node.message != null) {
diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart
index bbefd95..5aec3f7 100644
--- a/pkg/kernel/lib/visitor.dart
+++ b/pkg/kernel/lib/visitor.dart
@@ -51,6 +51,9 @@
   R visitBoolLiteral(BoolLiteral node) => defaultBasicLiteral(node);
   R visitNullLiteral(NullLiteral node) => defaultBasicLiteral(node);
   R visitLet(Let node) => defaultExpression(node);
+  R visitLoadLibrary(LoadLibrary node) => defaultExpression(node);
+  R visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) =>
+      defaultExpression(node);
 }
 
 abstract class StatementVisitor<R> {
@@ -155,6 +158,9 @@
   R visitBoolLiteral(BoolLiteral node) => defaultBasicLiteral(node);
   R visitNullLiteral(NullLiteral node) => defaultBasicLiteral(node);
   R visitLet(Let node) => defaultExpression(node);
+  R visitLoadLibrary(LoadLibrary node) => defaultExpression(node);
+  R visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) =>
+      defaultExpression(node);
 
   // Statements
   R defaultStatement(Statement node) => defaultTreeNode(node);
@@ -204,6 +210,7 @@
 
   // Other tree nodes
   R visitLibrary(Library node) => defaultTreeNode(node);
+  R visitDeferredImport(DeferredImport node) => defaultTreeNode(node);
   R visitTypeParameter(TypeParameter node) => defaultTreeNode(node);
   R visitFunctionNode(FunctionNode node) => defaultTreeNode(node);
   R visitArguments(Arguments node) => defaultTreeNode(node);
diff --git a/runtime/vm/kernel_binary.cc b/runtime/vm/kernel_binary.cc
index dc5c567..c2b8512 100644
--- a/runtime/vm/kernel_binary.cc
+++ b/runtime/vm/kernel_binary.cc
@@ -678,6 +678,10 @@
   source_uri_index_ = reader->ReadUInt();
   reader->set_current_script_id(source_uri_index_);
 
+  int num_imports = reader->ReadUInt();
+  if (num_imports != 0) {
+    FATAL("Deferred imports not implemented in VM");
+  }
   int num_classes = reader->ReadUInt();
   classes().EnsureInitialized(num_classes);
   for (int i = 0; i < num_classes; i++) {