Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(461)

Unified Diff: pkg/front_end/lib/src/fasta/kernel/kernel_outline_shaker.dart

Issue 2893493004: First step for modular output in fasta. (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
new file mode 100644
index 0000000000000000000000000000000000000000..3873795ccf6445c56911127b6ce00b36d34ea925
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_outline_shaker.dart
@@ -0,0 +1,433 @@
+// 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.
ahe 2017/05/22 12:04:24 Removes unnecessary libraries, classes, and member
+///
+/// This applies a simple "tree-shaking" technique: the full body of libraries
+/// whose URI match [isIncluded] are preserved, and so is the outline of the
+/// members and classes which are referred to transitively from these 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.
+///
+/// When [retainClassMembers] is true, all members of retained classes will be
+/// retained as well.
+void trimProgram(Program program, bool isIncluded(Uri uri),
+ {bool retainClassMembers: true}) {
+ var result = new _Analysis(program, isIncluded, retainClassMembers).run();
+ new _Shaker(result, isIncluded).transform(program);
+}
+
+/// Result of analyzing a program before tree-shaking it.
+abstract class _AnalysisResult {
+ /// 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);
+}
+
+/// Visitor used by [trimProgram] to collects which libraries, classes, and
+/// members should be preserved.
+class _Analysis extends RecursiveVisitor implements _AnalysisResult {
+ /// The program being analyzed.
+ final Program program;
+
+ /// Helper to fetch classes and members of the core libraries.
+ final CoreTypes coreTypes;
+
+ /// Filter to determine libraries that should be analyzed.
+ final Filter isIncluded;
+
+ /// When retaining a class, whether to retain all of its members.
+ // TODO(sigmund): delete this option once we figure out the long term plan. Do
+ // we need to do this? Today it seems necessary because some transformations
+ // require it, but maybe we don't need them all (e.g. only constructors and
+ // members whose inferenceTarget is referred do in the sources?) Do we need a
+ // different retention policy for including the type hierarchy with and
+ // without members?
+ final bool retainClassMembers;
+
+ /// 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>();
+
+ @override
+ bool isLibraryUsed(Library library) => _libraries.contains(library);
+
+ @override
+ bool isClassUsed(Class cls) => _classes.contains(cls);
+
+ @override
+ bool isMemberUsed(Member m) => _members.contains(m);
+
+ _Analysis(this.program, this.isIncluded, this.retainClassMembers)
+ : coreTypes = new CoreTypes(program);
+
+ _AnalysisResult run() {
+ _markRequired();
+ _markMember(program.mainMethod);
+ for (var library in program.libraries) {
+ if (isIncluded(library.importUri)) {
+ library.accept(this);
+ }
+ }
+ return this;
+ }
+
+ /// Marks classes and members that are assumed to exist by fasta or by
+ /// transformers.
+ void _markRequired() {
+ coreTypes.objectClass.members.forEach(_markMember);
+
+ // These are assumed to be available by fasta:
+ _markClass(coreTypes.objectClass);
+ _markClass(coreTypes.nullClass);
+ _markClass(coreTypes.boolClass);
+ _markClass(coreTypes.intClass);
+ _markClass(coreTypes.numClass);
+ _markClass(coreTypes.doubleClass);
+ _markClass(coreTypes.stringClass);
+ _markClass(coreTypes.listClass);
+ _markClass(coreTypes.mapClass);
+ _markClass(coreTypes.iterableClass);
+ _markClass(coreTypes.iteratorClass);
+ _markClass(coreTypes.futureClass);
+ _markClass(coreTypes.streamClass);
+ _markClass(coreTypes.symbolClass);
+ _markClass(coreTypes.internalSymbolClass);
+ _markClass(coreTypes.typeClass);
+ _markClass(coreTypes.functionClass);
+ _markClass(coreTypes.invocationClass);
+ _markMember(coreTypes.getMember("dart:_internal", "ExternalName", ""));
+
+ // These are needed by the continuation (async/await) transformer:
+ _markClass(coreTypes.getClass('dart:core', 'Iterator'));
+ _markClass(coreTypes.getClass('dart:async', 'Future'));
+ _markClass(coreTypes.getClass('dart:async', 'FutureOr'));
+ _markClass(coreTypes.getClass('dart:async', 'Completer'));
+ _markMember(coreTypes.getMember('dart:async', 'Completer', 'sync'));
+ _markMember(coreTypes.getMember('dart:core', '_SyncIterable', ''));
+ _markMember(coreTypes.getMember('dart:async', '_StreamIterator', ''));
+ _markMember(coreTypes.getMember('dart:async', 'Future', 'microtask'));
+ _markMember(
+ coreTypes.getMember('dart:async', '_AsyncStarStreamController', ''));
+ _markMember(coreTypes.getTopLevelMember('dart:core', 'print'));
+ _markMember(
+ coreTypes.getTopLevelMember('dart:async', '_asyncThenWrapperHelper'));
+ _markMember(
+ coreTypes.getTopLevelMember('dart:async', '_asyncErrorWrapperHelper'));
+ _markMember(coreTypes.getTopLevelMember('dart:async', '_awaitHelper'));
+
+ // These are needed by the mixin transformer
+ _markMember(coreTypes.getMember('dart:core', '_InvocationMirror', ''));
+ _markMember(coreTypes.getMember('dart:core', 'List', 'from'));
+ }
+
+ /// Mark a library as used.
+ _markLibrary(Library lib) {
+ if (!_libraries.add(lib)) return;
+ }
+
+ /// Mark a class and it's supertypes as used.
+ void _markClass(Class cls) {
+ if (cls == null || !_classes.add(cls)) return;
+ _markLibrary(cls.parent);
+ visitList(cls.annotations, this);
+ cls.supertype?.accept(this);
+ cls.mixedInType?.accept(this);
+ visitList(cls.implementedTypes, this);
+ visitList(cls.typeParameters, this);
+
+ if (retainClassMembers) cls.members.forEach(_markMember);
+ }
+
+ _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(this);
+ } else if (node is Procedure) {
+ _markFunctionInterface(node.function);
+ }
+ }
+
+ _markFunctionInterface(FunctionNode node) {
+ for (var parameter in node.typeParameters) {
+ parameter.bound.accept(this);
+ }
+ for (var parameter in node.positionalParameters) {
+ parameter.type.accept(this);
+ }
+ for (var parameter in node.namedParameters) {
+ parameter.type.accept(this);
+ }
+ node.returnType.accept(this);
+ }
+
+ @override
+ visitFunctionNode(FunctionNode node) {
+ switch (node.asyncMarker) {
+ case AsyncMarker.Sync:
+ break;
+ case AsyncMarker.SyncStar:
+ _markClass(coreTypes.iterableClass);
scheglov 2017/05/18 15:54:08 These classes are already included in _markRequire
Siggi Cherem (dart-lang) 2017/05/19 04:37:32 good point. I removed them from here. Also - now t
+ break;
+ case AsyncMarker.Async:
+ _markClass(coreTypes.futureClass);
+ break;
+ case AsyncMarker.AsyncStar:
+ _markClass(coreTypes.streamClass);
+ break;
+ case AsyncMarker.SyncYielding:
+ break;
+ }
+ node.visitChildren(this);
+ }
+
+ @override
+ visitSuperInitializer(SuperInitializer node) {
+ _markMember(node.target);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitRedirectingInitializer(RedirectingInitializer node) {
+ _markMember(node.target);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitConstructorInvocation(ConstructorInvocation node) {
+ _markMember(node.target);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitStaticInvocation(StaticInvocation node) {
+ _markMember(node.target);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitDirectMethodInvocation(DirectMethodInvocation node) {
+ if (node.receiver is! ThisExpression) {
+ return internalError('Direct calls are only supported on "this"');
+ }
+ _markMember(node.target);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitMethodInvocation(MethodInvocation node) {
+ _markMember(node.interfaceTarget);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitStaticGet(StaticGet node) {
+ _markMember(node.target);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitStaticSet(StaticSet node) {
+ _markMember(node.target);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitDirectPropertyGet(DirectPropertyGet node) {
+ _markMember(node.target);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitDirectPropertySet(DirectPropertySet node) {
+ _markMember(node.target);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitSuperPropertyGet(SuperPropertyGet node) {
+ _markMember(node.interfaceTarget);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitSuperPropertySet(SuperPropertySet node) {
+ _markMember(node.interfaceTarget);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitPropertyGet(PropertyGet node) {
+ _markMember(node.interfaceTarget);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitPropertySet(PropertySet node) {
+ _markMember(node.interfaceTarget);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitListLiteral(ListLiteral node) {
+ _markClass(coreTypes.listClass);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitMapLiteral(MapLiteral node) {
+ _markClass(coreTypes.mapClass);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitStringConcatenation(StringConcatenation node) {
+ node.visitChildren(this);
+ }
+
+ @override
+ visitInterfaceType(InterfaceType node) {
+ _markClass(node.classNode);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitSupertype(Supertype node) {
+ _markClass(node.classNode);
+ node.visitChildren(this);
+ }
+
+ @override
+ visitDoubleLiteral(DoubleLiteral node) {
+ _markClass(coreTypes.doubleClass);
+ }
+
+ @override
+ visitSymbolLiteral(SymbolLiteral node) {
+ _markClass(coreTypes.symbolClass);
+ }
+
+ @override
+ visitTypeLiteral(TypeLiteral node) {
+ _markClass(coreTypes.typeClass);
+ 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 [_Analysis] above. For every member in these
+/// excluded libraries, this transformer also removes function bodies and
+/// initializers.
+class _Shaker extends Transformer {
+ final _AnalysisResult result;
+ final Filter isIncluded;
+
+ _Shaker(this.result, this.isIncluded);
+
+ void transform(Program program) {
+ var toRemove = new Set<Library>();
+ for (var library in program.libraries) {
+ if (!isIncluded(library.importUri)) {
+ if (!result.isLibraryUsed(library)) {
+ toRemove.add(library);
+ } else {
+ library.isExternal = true;
+ library.transformChildren(this);
+ }
+ }
+ }
+ program.libraries.removeWhere(toRemove.contains);
+ }
+
+ Class visitClass(Class node) {
+ if (!result.isClassUsed(node)) {
+ node.canonicalName?.unbind();
+ return null; // Remove the class.
+ } else {
+ node.transformChildren(this);
+ return node;
+ }
+ }
+
+ Member defaultMember(Member node) {
+ if (!result.isMemberUsed(node)) {
+ node.canonicalName?.unbind();
+ return null;
+ } else {
+ if (node is Procedure) {
+ _clearFunction(node.function);
+ } else if (node is Field) {
+ node.initializer = null;
+ } else if (node is Constructor) {
+ node.initializers.clear();
+ _clearFunction(node.function);
+ }
+ return node;
+ }
+ }
+
+ _clearFunction(FunctionNode function) {
+ function.body = null;
+ // TODO(sigmund): Fix directly the continuation transformer. The async/await
+ // continuation transformer fails if it finds a function with no body that
+ // it is marked with async/async*/sync*, we shouldn't be patching that here.
+ function.dartAsyncMarker = function.asyncMarker;
+ function.asyncMarker = AsyncMarker.Sync;
+ }
+
+ /// 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 the
+ // analysis step is meant to detect this.
+ Typedef visitTypedef(Typedef node) => null;
+
+ TreeNode defaultTreeNode(TreeNode node) => node;
+}
+
+typedef bool Filter(Uri uri);

Powered by Google App Engine
This is Rietveld 408576698