| Index: pkg/front_end/lib/src/fasta/kernel/kernel_outline_shaker.dart
|
| diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_outline_shaker.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_outline_shaker.dart
|
| deleted file mode 100644
|
| index 67508365e99deeeffecb824415bf2bb9aaaca924..0000000000000000000000000000000000000000
|
| --- a/pkg/front_end/lib/src/fasta/kernel/kernel_outline_shaker.dart
|
| +++ /dev/null
|
| @@ -1,406 +0,0 @@
|
| -// Copyright (c) 2016, 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.
|
| -
|
| -/// A transformation to create a self-contained modular kernel without
|
| -/// unnecessary references to other libraries.
|
| -library fasta.kernel.kernel_outline_shaker;
|
| -
|
| -import 'package:kernel/ast.dart';
|
| -import 'package:kernel/core_types.dart';
|
| -
|
| -import '../errors.dart' show internalError;
|
| -
|
| -/// Removes from [program] unnecessary libraries, classes, and members.
|
| -///
|
| -/// This applies a simple "tree-shaking" technique: the full body of libraries
|
| -/// whose URI match [isIncluded] is preserved, and so is the outline of the
|
| -/// members and classes which are indicated by [data] (which should
|
| -/// practically include all members and classes transitively visible from the
|
| -/// included libraries).
|
| -///
|
| -/// The intent is that the resulting program has the entire code that is meant
|
| -/// to be included and the minimum required to prevent dangling references and
|
| -/// allow modular program transformations.
|
| -///
|
| -/// Note that the resulting program may include libraries not in [isIncluded],
|
| -/// but those will be marked as external. There should be no method bodies for
|
| -/// any members of those libraries.
|
| -void trimProgram(Program program, RetainedData data, bool isIncluded(Uri uri)) {
|
| - new KernelOutlineShaker(data, isIncluded).transform(program);
|
| -}
|
| -
|
| -/// Informs about which libraries, classes, and members should be retained by
|
| -/// the [KernelOutlineShaker] when tree-shaking.
|
| -abstract class RetainedData {
|
| - /// Whether a library should be preserved and mark as external.
|
| - bool isLibraryUsed(Library library);
|
| -
|
| - /// Whether a class should be preserved. If a class is preserved, its
|
| - /// supertypes will be preserved too, but some of it members may not be
|
| - /// included.
|
| - bool isClassUsed(Class cls);
|
| -
|
| - /// Whether a member should be preserved. If so, its enclosing class/library
|
| - /// will be preserved too.
|
| - bool isMemberUsed(Member member);
|
| -}
|
| -
|
| -/// A builder of [RetainedData] that recursively marks transitive dependencies.
|
| -///
|
| -/// This builder contains APIs to mark the roots that are needed (e.g.
|
| -/// [markClass] and [markMember]). Note this builder does not determine what
|
| -/// roots to keep, that is done either directly by fasta while it is parsing, or
|
| -/// by using a visitor like the [RootsMarker] below.
|
| -class RetainedDataBuilder extends RetainedData {
|
| - /// Libraries that contained code that is transitively reachable from the
|
| - /// included libraries.
|
| - final Set<Library> libraries = new Set<Library>();
|
| -
|
| - /// Classes that are transitively reachable from the included libraries.
|
| - final Set<Class> classes = new Set<Class>();
|
| -
|
| - /// Members that are transitively reachable from the included libraries.
|
| - final Set<Member> members = new Set<Member>();
|
| -
|
| - TypeMarker typeMarker;
|
| -
|
| - @override
|
| - bool isLibraryUsed(Library library) => libraries.contains(library);
|
| -
|
| - @override
|
| - bool isClassUsed(Class cls) => classes.contains(cls);
|
| -
|
| - @override
|
| - bool isMemberUsed(Member m) => members.contains(m);
|
| -
|
| - RetainedDataBuilder() {
|
| - typeMarker = new TypeMarker(this);
|
| - }
|
| -
|
| - /// Mark a library as used.
|
| - void markLibrary(Library lib) {
|
| - libraries.add(lib);
|
| - }
|
| -
|
| - /// Mark a class and it's supertypes as used.
|
| - void markClass(Class cls) {
|
| - if (cls == null || !classes.add(cls)) return;
|
| - markLibrary(cls.parent);
|
| - // TODO(sigmund): retain annotations?
|
| - // visitList(cls.annotations, this);
|
| - markSupertype(cls.supertype);
|
| - markSupertype(cls.mixedInType);
|
| - cls.implementedTypes.forEach(markSupertype);
|
| - cls.typeParameters.forEach((t) => t.bound.accept(typeMarker));
|
| - }
|
| -
|
| - /// Mark the class and type arguments of [node].
|
| - void markSupertype(Supertype node) {
|
| - if (node == null) return;
|
| - markClass(node.classNode);
|
| - node.typeArguments.forEach((t) => t.accept(typeMarker));
|
| - }
|
| -
|
| - /// Mark a member and types mentioned on its interface.
|
| - void markMember(Member m) {
|
| - if (m == null || !members.add(m)) return;
|
| - markMemberInterface(m);
|
| - var parent = m.parent;
|
| - if (parent is Library) {
|
| - markLibrary(parent);
|
| - } else if (parent is Class) {
|
| - markClass(parent);
|
| - }
|
| - }
|
| -
|
| - void markMemberInterface(Member node) {
|
| - if (node is Field) {
|
| - node.type.accept(typeMarker);
|
| - } else if (node is Procedure) {
|
| - var function = node.function;
|
| - function.typeParameters.forEach((p) => p.bound.accept(typeMarker));
|
| - function.positionalParameters.forEach((p) => p.type.accept(typeMarker));
|
| - function.namedParameters.forEach((p) => p.type.accept(typeMarker));
|
| - function.returnType.accept(typeMarker);
|
| - }
|
| - }
|
| -}
|
| -
|
| -/// A helper visitor used to mark transitive types by the [RetainedDataBuilder].
|
| -class TypeMarker extends DartTypeVisitor {
|
| - RetainedDataBuilder data;
|
| -
|
| - TypeMarker(this.data);
|
| -
|
| - visitInterfaceType(InterfaceType node) {
|
| - data.markClass(node.classNode);
|
| - node.typeArguments.forEach((t) => t.accept(this));
|
| - }
|
| -
|
| - visitFunctionType(FunctionType node) {
|
| - node.typeParameters.forEach((t) => t.bound.accept(this));
|
| - node.positionalParameters.forEach((t) => t.accept(this));
|
| - node.namedParameters.forEach((t) => t.type.accept(this));
|
| - node.returnType.accept(this);
|
| - }
|
| -
|
| - visitTypeParameterType(TypeParameterType node) {
|
| - // Note: node.parameter is marked by marking the enclosing element.
|
| - }
|
| -
|
| - visitTypedefType(TypedefType node) {
|
| - node.typeArguments.forEach((t) => t.accept(this));
|
| - }
|
| -}
|
| -
|
| -/// Determines the root APIs that need to be retained before running the
|
| -/// tree-shaker.
|
| -///
|
| -/// This is implemented using a visitor that walks through the sources that are
|
| -/// intended to be part of the kernel output.
|
| -// TODO(sigmund): delete. We should collect this information while
|
| -// building kernel without having to run a visitor afterwards.
|
| -class RootsMarker extends RecursiveVisitor {
|
| - final RetainedDataBuilder data;
|
| - RootsMarker(this.data);
|
| -
|
| - void run(Program program, bool isIncluded(Uri uri)) {
|
| - markRequired(program);
|
| - data.markMember(program.mainMethod);
|
| - for (var library in program.libraries) {
|
| - if (isIncluded(library.importUri)) {
|
| - library.accept(this);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /// Marks classes and members that are assumed to exist by fasta or by
|
| - /// transformers.
|
| - // TODO(sigmund): consider being more fine-grained and only marking what is
|
| - // seen and used.
|
| - void markRequired(Program program) {
|
| - var coreTypes = new CoreTypes(program);
|
| - coreTypes.objectClass.members.forEach(data.markMember);
|
| -
|
| - // These are assumed to be available by fasta:
|
| - data.markClass(coreTypes.objectClass);
|
| - data.markClass(coreTypes.nullClass);
|
| - data.markClass(coreTypes.boolClass);
|
| - data.markClass(coreTypes.intClass);
|
| - data.markClass(coreTypes.numClass);
|
| - data.markClass(coreTypes.doubleClass);
|
| - data.markClass(coreTypes.stringClass);
|
| - data.markClass(coreTypes.listClass);
|
| - data.markClass(coreTypes.mapClass);
|
| - data.markClass(coreTypes.iterableClass);
|
| - data.markClass(coreTypes.iteratorClass);
|
| - data.markClass(coreTypes.futureClass);
|
| - data.markClass(coreTypes.streamClass);
|
| - data.markClass(coreTypes.symbolClass);
|
| - data.markClass(coreTypes.internalSymbolClass);
|
| - data.markClass(coreTypes.typeClass);
|
| - data.markClass(coreTypes.functionClass);
|
| - data.markClass(coreTypes.invocationClass);
|
| - data.markMember(coreTypes.getMember("dart:_internal", "ExternalName", ""));
|
| -
|
| - // These are needed by the continuation (async/await) transformer:
|
| - data.markClass(coreTypes.getClass('dart:core', 'Iterator'));
|
| - data.markClass(coreTypes.getClass('dart:async', 'Future'));
|
| - data.markClass(coreTypes.getClass('dart:async', 'FutureOr'));
|
| - data.markClass(coreTypes.getClass('dart:async', 'Completer'));
|
| - data.markMember(coreTypes.getMember('dart:async', 'Completer', 'sync'));
|
| - data.markMember(coreTypes.getMember('dart:core', '_SyncIterable', ''));
|
| - data.markMember(coreTypes.getMember('dart:async', '_StreamIterator', ''));
|
| - data.markMember(coreTypes.getMember('dart:async', 'Future', 'microtask'));
|
| - data.markMember(
|
| - coreTypes.getMember('dart:async', '_AsyncStarStreamController', ''));
|
| - data.markMember(coreTypes.getTopLevelMember('dart:core', 'print'));
|
| - data.markMember(
|
| - coreTypes.getTopLevelMember('dart:async', '_asyncThenWrapperHelper'));
|
| - data.markMember(
|
| - coreTypes.getTopLevelMember('dart:async', '_asyncErrorWrapperHelper'));
|
| - data.markMember(coreTypes.getTopLevelMember('dart:async', '_awaitHelper'));
|
| -
|
| - // These are needed by the mixin transformer
|
| - data.markMember(coreTypes.getMember('dart:core', '_InvocationMirror', ''));
|
| - data.markMember(coreTypes.getMember('dart:core', 'List', 'from'));
|
| - }
|
| -
|
| - visitConstructor(Constructor node) {
|
| - if (!node.initializers.any((i) => i is SuperInitializer)) {
|
| - // super() is currently implicit.
|
| - for (var ctor in node.enclosingClass.supertype.classNode.constructors) {
|
| - if (ctor.name.name == '') data.markMember(ctor);
|
| - }
|
| - }
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitSuperInitializer(SuperInitializer node) {
|
| - data.markMember(node.target);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitRedirectingInitializer(RedirectingInitializer node) {
|
| - data.markMember(node.target);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitConstructorInvocation(ConstructorInvocation node) {
|
| - data.markMember(node.target);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitStaticInvocation(StaticInvocation node) {
|
| - data.markMember(node.target);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitDirectMethodInvocation(DirectMethodInvocation node) {
|
| - if (node.receiver is! ThisExpression) {
|
| - return internalError('Direct calls are only supported on "this"');
|
| - }
|
| - data.markMember(node.target);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitMethodInvocation(MethodInvocation node) {
|
| - data.markMember(node.interfaceTarget);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitStaticGet(StaticGet node) {
|
| - data.markMember(node.target);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitStaticSet(StaticSet node) {
|
| - data.markMember(node.target);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitDirectPropertyGet(DirectPropertyGet node) {
|
| - data.markMember(node.target);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitDirectPropertySet(DirectPropertySet node) {
|
| - data.markMember(node.target);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitSuperPropertyGet(SuperPropertyGet node) {
|
| - data.markMember(node.interfaceTarget);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitSuperPropertySet(SuperPropertySet node) {
|
| - data.markMember(node.interfaceTarget);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitPropertyGet(PropertyGet node) {
|
| - data.markMember(node.interfaceTarget);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitPropertySet(PropertySet node) {
|
| - data.markMember(node.interfaceTarget);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitInterfaceType(InterfaceType node) {
|
| - data.markClass(node.classNode);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitSupertype(Supertype node) {
|
| - data.markClass(node.classNode);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - @override
|
| - visitTypedefReference(Typedef node) {
|
| - return internalError('not implemented');
|
| - }
|
| -}
|
| -
|
| -/// Transformer that trims everything in the excluded libraries that is not
|
| -/// marked as preserved by the given [RetainedData]. For every member in these
|
| -/// excluded libraries, this transformer also removes function bodies and
|
| -/// initializers.
|
| -class KernelOutlineShaker extends Transformer {
|
| - final RetainedData data;
|
| - final Filter isIncluded;
|
| -
|
| - KernelOutlineShaker(this.data, this.isIncluded);
|
| -
|
| - void transform(Program program) {
|
| - var toRemove = new Set<Library>();
|
| - for (var library in program.libraries) {
|
| - if (!isIncluded(library.importUri)) {
|
| - if (!data.isLibraryUsed(library)) {
|
| - toRemove.add(library);
|
| - } else {
|
| - library.isExternal = true;
|
| - library.transformChildren(this);
|
| - }
|
| - }
|
| - }
|
| - program.libraries.removeWhere(toRemove.contains);
|
| - }
|
| -
|
| - Class visitClass(Class node) {
|
| - if (!data.isClassUsed(node)) {
|
| - node.canonicalName?.unbind();
|
| - return null; // Remove the class.
|
| - } else {
|
| - node.transformChildren(this);
|
| - return node;
|
| - }
|
| - }
|
| -
|
| - Member defaultMember(Member node) {
|
| - if (!data.isMemberUsed(node)) {
|
| - node.canonicalName?.unbind();
|
| - return null;
|
| - } else {
|
| - if (node is Procedure) {
|
| - node.function.body = null;
|
| - } else if (node is Field) {
|
| - node.initializer = null;
|
| - } else if (node is Constructor) {
|
| - node.initializers.clear();
|
| - node.function.body = null;
|
| - }
|
| - return node;
|
| - }
|
| - }
|
| -
|
| - /// Types appear to be encoded directly, so we have no need to preserve
|
| - /// typedefs.
|
| - // TODO(sigmund): revisit if this is not the case, the `inputError` in
|
| - // [RootsMarker] is meant to detect this.
|
| - Typedef visitTypedef(Typedef node) => null;
|
| -
|
| - TreeNode defaultTreeNode(TreeNode node) => node;
|
| -}
|
| -
|
| -typedef bool Filter(Uri uri);
|
|
|