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

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: revisit approach 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 } else if (node is Constructor) {
127 for (var s in node.initializers) {
128 if (s is SuperInitializer) {
129 var q = '222 marking ${s.target}';
130 if (q.contains("Converter")) print(q);
scheglov 2017/05/19 17:19:05 Debug code?
Siggi Cherem (dart-lang) 2017/05/19 19:43:30 ha, yes, thank you.
131 markMember(s.target);
132 }
133 }
134 }
135 }
136 }
137
138 /// A helper visitor used to mark transitive types by the [RetainedDataBuilder].
139 class TypeMarker extends DartTypeVisitor {
140 RetainedDataBuilder data;
141
142 TypeMarker(this.data);
143
144 visitInterfaceType(InterfaceType node) {
145 data.markClass(node.classNode);
146 node.typeArguments.forEach((t) => t.accept(this));
147 }
148
149 visitFunctionType(FunctionType node) {
150 node.typeParameters.forEach((t) => t.bound.accept(this));
151 node.positionalParameters.forEach((t) => t.accept(this));
152 node.namedParameters.forEach((t) => t.type.accept(this));
153 node.returnType.accept(this);
154 }
155
156 visitTypeParameterType(TypeParameterType node) {
157 // Note: node.parameter is marked by marking the enclosing element.
158 }
159
160 visitTypedefType(TypedefType node) {
161 node.typeArguments.forEach((t) => t.accept(this));
162 }
163 }
164
165 /// Determines the root APIs that need to be retained before running the
166 /// tree-shaker.
167 ///
168 /// This is implemented using a visitor that walks through the sources that are
169 /// intended to be part of the kernel output.
170 // TODO(sigmund): delete. We should collect this information while
171 // building kernel without having to run a visitor afterwards.
172 class RootsMarker extends RecursiveVisitor {
173 final RetainedDataBuilder data;
174 RootsMarker(this.data);
175
176 void run(Program program, bool isIncluded(Uri uri)) {
177 markRequired(program);
178 data.markMember(program.mainMethod);
179 for (var library in program.libraries) {
180 if (isIncluded(library.importUri)) {
181 library.accept(this);
182 }
183 }
184 }
185
186 /// Marks classes and members that are assumed to exist by fasta or by
187 /// transformers.
188 // TODO(sigmund): consider being more fine-grained and only marking what is
189 // seen and used.
190 void markRequired(Program program) {
191 var coreTypes = new CoreTypes(program);
192 coreTypes.objectClass.members.forEach(data.markMember);
193
194 // These are assumed to be available by fasta:
195 data.markClass(coreTypes.objectClass);
196 data.markClass(coreTypes.nullClass);
197 data.markClass(coreTypes.boolClass);
198 data.markClass(coreTypes.intClass);
199 data.markClass(coreTypes.numClass);
200 data.markClass(coreTypes.doubleClass);
201 data.markClass(coreTypes.stringClass);
202 data.markClass(coreTypes.listClass);
203 data.markClass(coreTypes.mapClass);
204 data.markClass(coreTypes.iterableClass);
205 data.markClass(coreTypes.iteratorClass);
206 data.markClass(coreTypes.futureClass);
207 data.markClass(coreTypes.streamClass);
208 data.markClass(coreTypes.symbolClass);
209 data.markClass(coreTypes.internalSymbolClass);
210 data.markClass(coreTypes.typeClass);
211 data.markClass(coreTypes.functionClass);
212 data.markClass(coreTypes.invocationClass);
213 data.markMember(coreTypes.getMember("dart:_internal", "ExternalName", ""));
214
215 // These are needed by the continuation (async/await) transformer:
216 data.markClass(coreTypes.getClass('dart:core', 'Iterator'));
217 data.markClass(coreTypes.getClass('dart:async', 'Future'));
218 data.markClass(coreTypes.getClass('dart:async', 'FutureOr'));
219 data.markClass(coreTypes.getClass('dart:async', 'Completer'));
220 data.markMember(coreTypes.getMember('dart:async', 'Completer', 'sync'));
221 data.markMember(coreTypes.getMember('dart:core', '_SyncIterable', ''));
222 data.markMember(coreTypes.getMember('dart:async', '_StreamIterator', ''));
223 data.markMember(coreTypes.getMember('dart:async', 'Future', 'microtask'));
224 data.markMember(
225 coreTypes.getMember('dart:async', '_AsyncStarStreamController', ''));
226 data.markMember(coreTypes.getTopLevelMember('dart:core', 'print'));
227 data.markMember(
228 coreTypes.getTopLevelMember('dart:async', '_asyncThenWrapperHelper'));
229 data.markMember(
230 coreTypes.getTopLevelMember('dart:async', '_asyncErrorWrapperHelper'));
231 data.markMember(coreTypes.getTopLevelMember('dart:async', '_awaitHelper'));
232
233 // These are needed by the mixin transformer
234 data.markMember(coreTypes.getMember('dart:core', '_InvocationMirror', ''));
235 data.markMember(coreTypes.getMember('dart:core', 'List', 'from'));
236 }
237
238 visitConstructor(Constructor node) {
239 if (!node.initializers.any((i) => i is SuperInitializer)) {
240 // super() is currently implicit.
241 for (var ctor in node.enclosingClass.supertype.classNode.constructors) {
242 if (ctor.name.name == '') data.markMember(ctor);
243 }
244 }
245 node.visitChildren(this);
246 }
247
248 @override
249 visitSuperInitializer(SuperInitializer node) {
250 var s = '>>> marking ${node.target}';
251 if (s.contains("Converter")) print(s);
scheglov 2017/05/19 17:19:05 Should we remove this debug code?
Siggi Cherem (dart-lang) 2017/05/19 19:43:30 yes, thank you
252 data.markMember(node.target);
253 node.visitChildren(this);
254 }
255
256 @override
257 visitRedirectingInitializer(RedirectingInitializer node) {
258 data.markMember(node.target);
259 node.visitChildren(this);
260 }
261
262 @override
263 visitConstructorInvocation(ConstructorInvocation node) {
264 data.markMember(node.target);
265 node.visitChildren(this);
266 }
267
268 @override
269 visitStaticInvocation(StaticInvocation node) {
270 data.markMember(node.target);
271 node.visitChildren(this);
272 }
273
274 @override
275 visitDirectMethodInvocation(DirectMethodInvocation node) {
276 if (node.receiver is! ThisExpression) {
277 return internalError('Direct calls are only supported on "this"');
278 }
279 data.markMember(node.target);
280 node.visitChildren(this);
281 }
282
283 @override
284 visitMethodInvocation(MethodInvocation node) {
285 data.markMember(node.interfaceTarget);
286 node.visitChildren(this);
287 }
288
289 @override
290 visitStaticGet(StaticGet node) {
291 data.markMember(node.target);
292 node.visitChildren(this);
293 }
294
295 @override
296 visitStaticSet(StaticSet node) {
297 data.markMember(node.target);
298 node.visitChildren(this);
299 }
300
301 @override
302 visitDirectPropertyGet(DirectPropertyGet node) {
303 data.markMember(node.target);
304 node.visitChildren(this);
305 }
306
307 @override
308 visitDirectPropertySet(DirectPropertySet node) {
309 data.markMember(node.target);
310 node.visitChildren(this);
311 }
312
313 @override
314 visitSuperPropertyGet(SuperPropertyGet node) {
315 data.markMember(node.interfaceTarget);
316 node.visitChildren(this);
317 }
318
319 @override
320 visitSuperPropertySet(SuperPropertySet node) {
321 data.markMember(node.interfaceTarget);
322 node.visitChildren(this);
323 }
324
325 @override
326 visitPropertyGet(PropertyGet node) {
327 data.markMember(node.interfaceTarget);
328 node.visitChildren(this);
329 }
330
331 @override
332 visitPropertySet(PropertySet node) {
333 data.markMember(node.interfaceTarget);
334 node.visitChildren(this);
335 }
336
337 @override
338 visitInterfaceType(InterfaceType node) {
339 data.markClass(node.classNode);
340 node.visitChildren(this);
341 }
342
343 @override
344 visitSupertype(Supertype node) {
345 data.markClass(node.classNode);
346 node.visitChildren(this);
347 }
348
349 @override
350 visitTypedefReference(Typedef node) {
351 return internalError('not implemented');
352 }
353 }
354
355 /// Transformer that trims everything in the excluded libraries that is not
356 /// marked as preserved by the given [RetainedData]. For every member in these
357 /// excluded libraries, this transformer also removes function bodies and
358 /// initializers.
359 class KernelOutlineShaker extends Transformer {
360 final RetainedData data;
361 final Filter isIncluded;
362
363 KernelOutlineShaker(this.data, this.isIncluded);
364
365 void transform(Program program) {
366 var toRemove = new Set<Library>();
367 for (var library in program.libraries) {
368 if (!isIncluded(library.importUri)) {
369 if (!data.isLibraryUsed(library)) {
370 toRemove.add(library);
371 } else {
372 library.isExternal = true;
373 library.transformChildren(this);
374 }
375 }
376 }
377 program.libraries.removeWhere(toRemove.contains);
378 }
379
380 Class visitClass(Class node) {
381 if (!data.isClassUsed(node)) {
382 node.canonicalName?.unbind();
383 return null; // Remove the class.
384 } else {
385 node.transformChildren(this);
386 return node;
387 }
388 }
389
390 Member defaultMember(Member node) {
391 if (!data.isMemberUsed(node)) {
392 node.canonicalName?.unbind();
393 return null;
394 } else {
395 if (node is Procedure) {
396 node.function.body = null;
397 } else if (node is Field) {
398 node.initializer = null;
399 } else if (node is Constructor) {
400 node.initializers.clear();
401 node.function.body = null;
402 }
403 return node;
404 }
405 }
406
407 /// Types appear to be encoded directly, so we have no need to preserve
408 /// typedefs.
409 // TODO(sigmund): revisit if this is not the case, the `inputError` in
410 // the rootMarker is meant to detect this.
scheglov 2017/05/19 17:19:05 What is "rootMarker"?
Siggi Cherem (dart-lang) 2017/05/19 19:43:30 fixed - should be [RootsMarker] the class defined
411 Typedef visitTypedef(Typedef node) => null;
412
413 TreeNode defaultTreeNode(TreeNode node) => node;
414 }
415
416 typedef bool Filter(Uri uri);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698