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

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

Issue 2893493004: First step for modular output in fasta. (Closed)
Patch Set: cl comments 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /// A transformation to create a self-contained modular kernel without
6 /// unnecessary references to other libraries.
7 library fasta.kernel.kernel_outline_shaker;
8
9 import 'package:kernel/ast.dart';
10 import 'package:kernel/core_types.dart';
11
12 import '../errors.dart' show internalError;
13
14 /// Removes from [program] unnecessary libraries, classes, and members.
15 ///
16 /// This applies a simple "tree-shaking" technique: the full body of libraries
17 /// whose URI match [isIncluded] is preserved, and so is the outline of the
18 /// members and classes which are indicated by [data] (which should
19 /// practically include all members and classes transitively visible from the
20 /// included libraries).
21 ///
22 /// The intent is that the resulting program has the entire code that is meant
23 /// to be included and the minimum required to prevent dangling references and
24 /// allow modular program transformations.
25 ///
26 /// Note that the resulting program may include libraries not in [isIncluded],
27 /// but those will be marked as external. There should be no method bodies for
28 /// any members of those libraries.
29 void trimProgram(Program program, RetainedData data, bool isIncluded(Uri uri)) {
30 new KernelOutlineShaker(data, isIncluded).transform(program);
31 }
32
33 /// Informs about which libraries, classes, and members should be retained by
34 /// the [KernelOutlineShaker] when tree-shaking.
35 abstract class RetainedData {
36 /// Whether a library should be preserved and mark as external.
37 bool isLibraryUsed(Library library);
38
39 /// Whether a class should be preserved. If a class is preserved, its
40 /// supertypes will be preserved too, but some of it members may not be
41 /// included.
42 bool isClassUsed(Class cls);
43
44 /// Whether a member should be preserved. If so, its enclosing class/library
45 /// will be preserved too.
46 bool isMemberUsed(Member member);
47 }
48
49 /// A builder of [RetainedData] that recursively marks transitive dependencies.
50 ///
51 /// This builder contains APIs to mark the roots that are needed (e.g.
52 /// [markClass] and [markMember]). Note this builder does not determine what
53 /// roots to keep, that is done either directly by fasta while it is parsing, or
54 /// by using a visitor like the [RootsMarker] below.
55 class RetainedDataBuilder extends RetainedData {
56 /// Libraries that contained code that is transitively reachable from the
57 /// included libraries.
58 final Set<Library> libraries = new Set<Library>();
59
60 /// Classes that are transitively reachable from the included libraries.
61 final Set<Class> classes = new Set<Class>();
62
63 /// Members that are transitively reachable from the included libraries.
64 final Set<Member> members = new Set<Member>();
65
66 TypeMarker typeMarker;
67
68 @override
69 bool isLibraryUsed(Library library) => libraries.contains(library);
70
71 @override
72 bool isClassUsed(Class cls) => classes.contains(cls);
73
74 @override
75 bool isMemberUsed(Member m) => members.contains(m);
76
77 RetainedDataBuilder() {
78 typeMarker = new TypeMarker(this);
79 }
80
81 /// Mark a library as used.
82 void markLibrary(Library lib) {
83 libraries.add(lib);
84 }
85
86 /// Mark a class and it's supertypes as used.
87 void markClass(Class cls) {
88 if (cls == null || !classes.add(cls)) return;
89 markLibrary(cls.parent);
90 // TODO(sigmund): retain annotations?
91 // visitList(cls.annotations, this);
92 markSupertype(cls.supertype);
93 markSupertype(cls.mixedInType);
94 cls.implementedTypes.forEach(markSupertype);
95 cls.typeParameters.forEach((t) => t.bound.accept(typeMarker));
96 }
97
98 /// Mark the class and type arguments of [node].
99 void markSupertype(Supertype node) {
100 if (node == null) return;
101 markClass(node.classNode);
102 node.typeArguments.forEach((t) => t.accept(typeMarker));
103 }
104
105 /// Mark a member and types mentioned on its interface.
106 void markMember(Member m) {
107 if (m == null || !members.add(m)) return;
108 markMemberInterface(m);
109 var parent = m.parent;
110 if (parent is Library) {
111 markLibrary(parent);
112 } else if (parent is Class) {
113 markClass(parent);
114 }
115 }
116
117 void markMemberInterface(Member node) {
118 if (node is Field) {
119 node.type.accept(typeMarker);
120 } else if (node is Procedure) {
121 var function = node.function;
122 function.typeParameters.forEach((p) => p.bound.accept(typeMarker));
123 function.positionalParameters.forEach((p) => p.type.accept(typeMarker));
124 function.namedParameters.forEach((p) => p.type.accept(typeMarker));
125 function.returnType.accept(typeMarker);
126 }
127 }
128 }
129
130 /// A helper visitor used to mark transitive types by the [RetainedDataBuilder].
131 class TypeMarker extends DartTypeVisitor {
132 RetainedDataBuilder data;
133
134 TypeMarker(this.data);
135
136 visitInterfaceType(InterfaceType node) {
137 data.markClass(node.classNode);
138 node.typeArguments.forEach((t) => t.accept(this));
139 }
140
141 visitFunctionType(FunctionType node) {
142 node.typeParameters.forEach((t) => t.bound.accept(this));
143 node.positionalParameters.forEach((t) => t.accept(this));
144 node.namedParameters.forEach((t) => t.type.accept(this));
145 node.returnType.accept(this);
146 }
147
148 visitTypeParameterType(TypeParameterType node) {
149 // Note: node.parameter is marked by marking the enclosing element.
150 }
151
152 visitTypedefType(TypedefType node) {
153 node.typeArguments.forEach((t) => t.accept(this));
154 }
155 }
156
157 /// Determines the root APIs that need to be retained before running the
158 /// tree-shaker.
159 ///
160 /// This is implemented using a visitor that walks through the sources that are
161 /// intended to be part of the kernel output.
162 // TODO(sigmund): delete. We should collect this information while
163 // building kernel without having to run a visitor afterwards.
164 class RootsMarker extends RecursiveVisitor {
165 final RetainedDataBuilder data;
166 RootsMarker(this.data);
167
168 void run(Program program, bool isIncluded(Uri uri)) {
169 markRequired(program);
170 data.markMember(program.mainMethod);
171 for (var library in program.libraries) {
172 if (isIncluded(library.importUri)) {
173 library.accept(this);
174 }
175 }
176 }
177
178 /// Marks classes and members that are assumed to exist by fasta or by
179 /// transformers.
180 // TODO(sigmund): consider being more fine-grained and only marking what is
181 // seen and used.
182 void markRequired(Program program) {
183 var coreTypes = new CoreTypes(program);
184 coreTypes.objectClass.members.forEach(data.markMember);
185
186 // These are assumed to be available by fasta:
187 data.markClass(coreTypes.objectClass);
188 data.markClass(coreTypes.nullClass);
189 data.markClass(coreTypes.boolClass);
190 data.markClass(coreTypes.intClass);
191 data.markClass(coreTypes.numClass);
192 data.markClass(coreTypes.doubleClass);
193 data.markClass(coreTypes.stringClass);
194 data.markClass(coreTypes.listClass);
195 data.markClass(coreTypes.mapClass);
196 data.markClass(coreTypes.iterableClass);
197 data.markClass(coreTypes.iteratorClass);
198 data.markClass(coreTypes.futureClass);
199 data.markClass(coreTypes.streamClass);
200 data.markClass(coreTypes.symbolClass);
201 data.markClass(coreTypes.internalSymbolClass);
202 data.markClass(coreTypes.typeClass);
203 data.markClass(coreTypes.functionClass);
204 data.markClass(coreTypes.invocationClass);
205 data.markMember(coreTypes.getMember("dart:_internal", "ExternalName", ""));
206
207 // These are needed by the continuation (async/await) transformer:
208 data.markClass(coreTypes.getClass('dart:core', 'Iterator'));
209 data.markClass(coreTypes.getClass('dart:async', 'Future'));
210 data.markClass(coreTypes.getClass('dart:async', 'FutureOr'));
211 data.markClass(coreTypes.getClass('dart:async', 'Completer'));
212 data.markMember(coreTypes.getMember('dart:async', 'Completer', 'sync'));
213 data.markMember(coreTypes.getMember('dart:core', '_SyncIterable', ''));
214 data.markMember(coreTypes.getMember('dart:async', '_StreamIterator', ''));
215 data.markMember(coreTypes.getMember('dart:async', 'Future', 'microtask'));
216 data.markMember(
217 coreTypes.getMember('dart:async', '_AsyncStarStreamController', ''));
218 data.markMember(coreTypes.getTopLevelMember('dart:core', 'print'));
219 data.markMember(
220 coreTypes.getTopLevelMember('dart:async', '_asyncThenWrapperHelper'));
221 data.markMember(
222 coreTypes.getTopLevelMember('dart:async', '_asyncErrorWrapperHelper'));
223 data.markMember(coreTypes.getTopLevelMember('dart:async', '_awaitHelper'));
224
225 // These are needed by the mixin transformer
226 data.markMember(coreTypes.getMember('dart:core', '_InvocationMirror', ''));
227 data.markMember(coreTypes.getMember('dart:core', 'List', 'from'));
228 }
229
230 visitConstructor(Constructor node) {
231 if (!node.initializers.any((i) => i is SuperInitializer)) {
232 // super() is currently implicit.
233 for (var ctor in node.enclosingClass.supertype.classNode.constructors) {
234 if (ctor.name.name == '') data.markMember(ctor);
235 }
236 }
237 node.visitChildren(this);
238 }
239
240 @override
241 visitSuperInitializer(SuperInitializer node) {
242 data.markMember(node.target);
243 node.visitChildren(this);
244 }
245
246 @override
247 visitRedirectingInitializer(RedirectingInitializer node) {
248 data.markMember(node.target);
249 node.visitChildren(this);
250 }
251
252 @override
253 visitConstructorInvocation(ConstructorInvocation node) {
254 data.markMember(node.target);
255 node.visitChildren(this);
256 }
257
258 @override
259 visitStaticInvocation(StaticInvocation node) {
260 data.markMember(node.target);
261 node.visitChildren(this);
262 }
263
264 @override
265 visitDirectMethodInvocation(DirectMethodInvocation node) {
266 if (node.receiver is! ThisExpression) {
267 return internalError('Direct calls are only supported on "this"');
268 }
269 data.markMember(node.target);
270 node.visitChildren(this);
271 }
272
273 @override
274 visitMethodInvocation(MethodInvocation node) {
275 data.markMember(node.interfaceTarget);
276 node.visitChildren(this);
277 }
278
279 @override
280 visitStaticGet(StaticGet node) {
281 data.markMember(node.target);
282 node.visitChildren(this);
283 }
284
285 @override
286 visitStaticSet(StaticSet node) {
287 data.markMember(node.target);
288 node.visitChildren(this);
289 }
290
291 @override
292 visitDirectPropertyGet(DirectPropertyGet node) {
293 data.markMember(node.target);
294 node.visitChildren(this);
295 }
296
297 @override
298 visitDirectPropertySet(DirectPropertySet node) {
299 data.markMember(node.target);
300 node.visitChildren(this);
301 }
302
303 @override
304 visitSuperPropertyGet(SuperPropertyGet node) {
305 data.markMember(node.interfaceTarget);
306 node.visitChildren(this);
307 }
308
309 @override
310 visitSuperPropertySet(SuperPropertySet node) {
311 data.markMember(node.interfaceTarget);
312 node.visitChildren(this);
313 }
314
315 @override
316 visitPropertyGet(PropertyGet node) {
317 data.markMember(node.interfaceTarget);
318 node.visitChildren(this);
319 }
320
321 @override
322 visitPropertySet(PropertySet node) {
323 data.markMember(node.interfaceTarget);
324 node.visitChildren(this);
325 }
326
327 @override
328 visitInterfaceType(InterfaceType node) {
329 data.markClass(node.classNode);
330 node.visitChildren(this);
331 }
332
333 @override
334 visitSupertype(Supertype node) {
335 data.markClass(node.classNode);
336 node.visitChildren(this);
337 }
338
339 @override
340 visitTypedefReference(Typedef node) {
341 return internalError('not implemented');
342 }
343 }
344
345 /// Transformer that trims everything in the excluded libraries that is not
346 /// marked as preserved by the given [RetainedData]. For every member in these
347 /// excluded libraries, this transformer also removes function bodies and
348 /// initializers.
349 class KernelOutlineShaker extends Transformer {
350 final RetainedData data;
351 final Filter isIncluded;
352
353 KernelOutlineShaker(this.data, this.isIncluded);
354
355 void transform(Program program) {
356 var toRemove = new Set<Library>();
357 for (var library in program.libraries) {
358 if (!isIncluded(library.importUri)) {
359 if (!data.isLibraryUsed(library)) {
360 toRemove.add(library);
361 } else {
362 library.isExternal = true;
363 library.transformChildren(this);
364 }
365 }
366 }
367 program.libraries.removeWhere(toRemove.contains);
368 }
369
370 Class visitClass(Class node) {
371 if (!data.isClassUsed(node)) {
372 node.canonicalName?.unbind();
373 return null; // Remove the class.
374 } else {
375 node.transformChildren(this);
376 return node;
377 }
378 }
379
380 Member defaultMember(Member node) {
381 if (!data.isMemberUsed(node)) {
382 node.canonicalName?.unbind();
383 return null;
384 } else {
385 if (node is Procedure) {
386 node.function.body = null;
387 } else if (node is Field) {
388 node.initializer = null;
389 } else if (node is Constructor) {
390 node.initializers.clear();
391 node.function.body = null;
392 }
393 return node;
394 }
395 }
396
397 /// Types appear to be encoded directly, so we have no need to preserve
398 /// typedefs.
399 // TODO(sigmund): revisit if this is not the case, the `inputError` in
400 // [RootsMarker] is meant to detect this.
401 Typedef visitTypedef(Typedef node) => null;
402
403 TreeNode defaultTreeNode(TreeNode node) => node;
404 }
405
406 typedef bool Filter(Uri uri);
OLDNEW
« no previous file with comments | « pkg/front_end/lib/src/base/processed_options.dart ('k') | pkg/front_end/lib/src/fasta/kernel/kernel_target.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698