6cc943957b
The partitioning has the property that for every reference from loading unit S to unit T, either - the reference is through a deferred prefix, or - T is guarenteed to be loaded before S The partitioning is constructed by cutting the dominator tree of the library import graph at deferred imports. It is suboptimal if there are deferred imports to multiple libraries in a connected component, but the expected use case involves loading units with a single deferred entry. Bug: https://github.com/dart-lang/sdk/issues/41974 Change-Id: I3c49044ae19112525f197b077b36e4ce874b81ac Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151470 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
119 lines
3.4 KiB
Dart
119 lines
3.4 KiB
Dart
// Copyright (c) 2020, 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.
|
|
|
|
library vm.transformations.deferred_loading;
|
|
|
|
import 'package:kernel/ast.dart';
|
|
import '../dominators.dart';
|
|
import '../metadata/loading_units.dart';
|
|
|
|
class _LoadingUnitBuilder {
|
|
final int id;
|
|
final _LibraryVertex root;
|
|
final List<Library> members = <Library>[];
|
|
|
|
_LoadingUnitBuilder(this.id, this.root);
|
|
|
|
int get parentId =>
|
|
root.dominator == null ? 0 : root.dominator.loadingUnit.id;
|
|
|
|
LoadingUnit asLoadingUnit() {
|
|
return new LoadingUnit(
|
|
id, parentId, members.map((l) => l.importUri.toString()).toList());
|
|
}
|
|
|
|
String toString() =>
|
|
"_LoadingUnitBuilder(id=$id, parent=${parentId}, size=${members.length})";
|
|
}
|
|
|
|
class _LibraryVertex extends Vertex<_LibraryVertex> {
|
|
final Library library;
|
|
bool isLoadingRoot = true;
|
|
_LoadingUnitBuilder loadingUnit;
|
|
_LibraryVertex(this.library);
|
|
|
|
String toString() => "_LibraryVertex(${library.importUri})";
|
|
}
|
|
|
|
List<LoadingUnit> computeLoadingUnits(Component component) {
|
|
// 1. Build the dominator tree for the library import graph.
|
|
final map = <Library, _LibraryVertex>{};
|
|
for (final lib in component.libraries) {
|
|
map[lib] = new _LibraryVertex(lib);
|
|
}
|
|
for (final vertex in map.values) {
|
|
for (final dep in vertex.library.dependencies) {
|
|
final target = map[dep.targetLibrary];
|
|
vertex.successors.add(target);
|
|
}
|
|
}
|
|
final root = map[component.mainMethod.parent as Library];
|
|
|
|
// Fake imports from root library to every core library so they end up in
|
|
// the same loading unit attributed to the user's root library.
|
|
for (final vertex in map.values) {
|
|
if (vertex.library.importUri.scheme == "dart") {
|
|
root.successors.add(vertex);
|
|
vertex.isLoadingRoot = false;
|
|
}
|
|
}
|
|
|
|
computeDominators(root);
|
|
|
|
// 2. Find loading unit roots.
|
|
for (var importer in map.values) {
|
|
if ((importer != root) && (importer.dominator == null)) {
|
|
continue; // Unreachable library.
|
|
}
|
|
for (var dep in importer.library.dependencies) {
|
|
if (dep.isDeferred) {
|
|
continue;
|
|
}
|
|
var importee = map[dep.targetLibrary];
|
|
if (importer.isDominatedBy(importee)) {
|
|
continue;
|
|
}
|
|
importee.isLoadingRoot = false;
|
|
}
|
|
}
|
|
assert(root.isLoadingRoot);
|
|
|
|
var loadingUnits = <_LoadingUnitBuilder>[];
|
|
for (var vertex in map.values) {
|
|
if (vertex.isLoadingRoot) {
|
|
var unit = new _LoadingUnitBuilder(loadingUnits.length + 1, vertex);
|
|
vertex.loadingUnit = unit;
|
|
unit.members.add(vertex.library);
|
|
loadingUnits.add(unit);
|
|
}
|
|
}
|
|
|
|
// 3. Attribute every library to the dominating loading unit.
|
|
for (var vertex in map.values) {
|
|
if (vertex.isLoadingRoot) {
|
|
continue; // Already processed.
|
|
}
|
|
var dom = vertex.dominator;
|
|
if (dom == null) {
|
|
continue; // Unreachable library.
|
|
}
|
|
while (dom.loadingUnit == null) {
|
|
dom = dom.dominator;
|
|
}
|
|
vertex.loadingUnit = dom.loadingUnit;
|
|
vertex.loadingUnit.members.add(vertex.library);
|
|
}
|
|
|
|
return loadingUnits.map((u) => u.asLoadingUnit()).toList();
|
|
}
|
|
|
|
Component transformComponent(Component component) {
|
|
final metadata = new LoadingUnitsMetadata(computeLoadingUnits(component));
|
|
final repo = new LoadingUnitsMetadataRepository();
|
|
component.addMetadataRepository(repo);
|
|
repo.mapping[component] = metadata;
|
|
|
|
return component;
|
|
}
|