Files
sdk/pkg/vm/lib/transformations/deferred_loading.dart
T
Ryan Macnak 93a4247af3 Reapply "Account for @pragma("vm:entry-point") creating additional "root" libraries when partitioning the program into loading units."
Weaken assertion in gen_snapshot requiring all libraries to have a loading unit as there can still be unreachable libraries:
  - Google3 and Fuchsia will compile all the sources in a package to a single dill file, then present multiple input dill files to the AOT compilation. Since the set of libraries was derived from package membership instead of imports, many can be unreachable.
  - When the root library's main comes from an export, the frontend's representation will incorrectly report the library containing main as the root library and the true root library may be unreachable from it.

Instead, assert only that surviving compiled code is assigned a loading unit.

TEST=gallery
Bug: https://github.com/flutter/gallery/issues/545
Bug: https://github.com/dart-lang/sdk/issues/49325
Bug: https://github.com/dart-lang/sdk/issues/41974
Bug: b/237016312
Change-Id: Ia52563a6f517308d041368be11dcc85270f19acc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/249724
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2022-06-27 18:56:12 +00:00

186 lines
5.3 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 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/target/targets.dart' show Target;
import '../dominators.dart';
import '../metadata/loading_units.dart';
import 'pragma.dart';
class _LoadingUnitBuilder {
late int id;
final _LibraryVertex root;
final List<Library> members = <Library>[];
final List<_LoadingUnitBuilder> children = <_LoadingUnitBuilder>[];
_LoadingUnitBuilder(this.root);
_LoadingUnitBuilder? get parent => root.dominator?.loadingUnit;
int get parentId => parent == null ? 0 : parent!.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})";
}
class HasEntryPointVisitor extends RecursiveVisitor {
final PragmaAnnotationParser parser;
bool _hasEntryPoint = false;
HasEntryPointVisitor(this.parser);
visitAnnotations(List<Expression> annotations) {
for (var ann in annotations) {
ParsedPragma? pragma = parser.parsePragma(ann);
if (pragma is ParsedEntryPointPragma) {
_hasEntryPoint = true;
return;
}
}
}
@override
visitClass(Class klass) {
visitAnnotations(klass.annotations);
klass.visitChildren(this);
}
@override
defaultMember(Member node) {
visitAnnotations(node.annotations);
}
bool hasEntryPoint(Library lib) {
_hasEntryPoint = false;
visitLibrary(lib);
return _hasEntryPoint;
}
}
List<LoadingUnit> computeLoadingUnits(
Component component, HasEntryPointVisitor visitor) {
// 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!.enclosingLibrary]!;
// Fake imports from root library to every core library or library containing
// an entry point pragma so that they end up in the same loading unit
// attributed to the user's root library.
for (final vertex in map.values) {
if (vertex == root) {
continue;
}
if (vertex.library.importUri.isScheme("dart") ||
visitor.hasEntryPoint(vertex.library)) {
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);
final List<_LoadingUnitBuilder> loadingUnits = <_LoadingUnitBuilder>[];
for (var vertex in map.values) {
if (vertex.isLoadingRoot) {
var unit = new _LoadingUnitBuilder(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);
}
// 4. Sort loading units so parents are before children. Normally this order
// would already exist as a side effect of loading sources in import order,
// but this isn't guaranteed when combining separately produced kernel files.
for (var unit in loadingUnits) {
var parent = unit.parent;
if (parent != null) {
parent.children.add(unit);
}
}
var index = 0;
loadingUnits.clear();
loadingUnits.add(root.loadingUnit!);
while (index < loadingUnits.length) {
var unit = loadingUnits[index];
unit.id = ++index;
loadingUnits.addAll(unit.children);
}
return loadingUnits.map((u) => u.asLoadingUnit()).toList();
}
Component transformComponent(
Component component, CoreTypes coreTypes, Target target) {
final parser = ConstantPragmaAnnotationParser(coreTypes, target);
final visitor = HasEntryPointVisitor(parser);
final metadata =
new LoadingUnitsMetadata(computeLoadingUnits(component, visitor));
final repo = new LoadingUnitsMetadataRepository();
component.addMetadataRepository(repo);
repo.mapping[component] = metadata;
return component;
}