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++) {