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

Side by Side Diff: pkg/analyzer/lib/src/summary/link.dart

Issue 1828543009: First steps toward generating fully linked summaries from ASTs. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 9 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
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/summary/prelink.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 /**
6 * This library is capable of producing linked summaries from unlinked
7 * ones (or prelinked ones). It functions by building a miniature
8 * element model to represent the contents of the summaries, and then
9 * scanning the element model to gather linked information and adding
10 * it to the summary data structures.
11 *
12 * The reason we use a miniature element model to do the linking
13 * (rather than resynthesizing the full element model from the
14 * summaries) is that it is expected that we will only need to
15 * traverse a small subset of the element properties in order to link.
16 * Resynthesizing only those properties that we need should save
17 * substantial CPU time.
18 *
19 * The element model implements the same interfaces as the full
20 * element model, so we can re-use code elsewhere in the analysis
21 * engine to do the linking. However, only a small subset of the
22 * methods and getters defined in the full element model are
23 * implemented here. To avoid static warnings, each element model
24 * class contains an implementation of `noSuchMethod`.
25 *
26 * The miniature element model follows the following design
27 * principles:
28 *
29 * - With few exceptions, resynthesis is done incrementally on demand,
30 * so that we don't pay the cost of resynthesizing elements (or
31 * properties of elements) that aren't referenced from a part of the
32 * element model that is relevant to linking.
33 *
34 * - Computation of values in the miniature element model is similar
35 * to the task model, but much lighter weight. Instead of declaring
36 * tasks and their relationships using classes, each task is simply
37 * a method (frequently a getter) that computes a value. Instead of
38 * using a general purpose cache, values are cached by the methods
39 * themselves in private fields (with `null` typically representing
40 * "not yet cached").
41 *
42 * - No attempt is made to detect cyclic dependencies due to bugs in
43 * the analyzer. This saves time because dependency evaluation
44 * doesn't have to be a separate step from evaluating a value; we
45 * can simply call the getter.
46 *
47 * - However, for cases where cyclic dependencies may occur in the
48 * absence of analyzer bugs (e.g. because of errors in the code
49 * being analyzed, or cycles between top level and static variables
50 * undergoing type inference), we do precompute dependencies, and we
51 * use Tarjan's strongly connected components algorithm to detect
52 * cycles.
53 *
54 * - As much as possible, bookkeeping data is pointed to directly by
55 * the element objects, rather than being stored in maps.
56 *
57 * - Where possible, we favor method dispatch instead of "is" and "as"
58 * checks. E.g. see [ReferenceableElementForLink.asConstructor].
59 */
60
61 import 'package:analyzer/dart/element/element.dart';
62 import 'package:analyzer/dart/element/type.dart';
63 import 'package:analyzer/src/generated/utilities_dart.dart';
64 import 'package:analyzer/src/summary/format.dart';
65 import 'package:analyzer/src/summary/idl.dart';
66 import 'package:analyzer/src/summary/prelink.dart';
67
68 /**
69 * Link together the build unit consisting of [libraryUris], using
70 * [getDependency] to fetch the [LinkedLibrary] objects from other
71 * build units, and [getUnit] to fetch the [UnlinkedUnit] objects from
72 * both this build unit and other build units.
73 *
74 * A map is returned whose keys are the URIs of the libraries in this
75 * build unit, and whose values are the corresponding
76 * [LinkedLibraryBuilder]s.
77 */
78 Map<String, LinkedLibraryBuilder> link(Set<String> libraryUris,
79 GetDependencyCallback getDependency, GetUnitCallback getUnit) {
80 Map<String, LinkedLibraryBuilder> linkedLibraries =
81 <String, LinkedLibraryBuilder>{};
82 for (String absoluteUri in libraryUris) {
83 Uri uri = Uri.parse(absoluteUri);
84 UnlinkedUnit getRelativeUnit(String relativeUri) =>
85 getUnit(resolveRelativeUri(uri, Uri.parse(relativeUri)).toString());
86 linkedLibraries[absoluteUri] = prelink(
87 getUnit(absoluteUri),
88 getRelativeUnit,
89 (String relativeUri) => getRelativeUnit(relativeUri)?.publicNamespace);
90 }
91 relink(linkedLibraries, getDependency, getUnit);
92 return linkedLibraries;
93 }
94
95 /**
96 * Given [libraries] (a map from URI to [LinkedLibraryBuilder]
97 * containing correct prelinked information), rebuild linked
98 * information, using [getDependency] to fetch the [LinkedLibrary]
99 * objects from other build units, and [getUnit] to fetch the
100 * [UnlinkedUnit] objects from both this build unit and other build
101 * units.
102 */
103 void relink(Map<String, LinkedLibraryBuilder> libraries,
104 GetDependencyCallback getDependency, GetUnitCallback getUnit) {
105 new _Linker(libraries, getDependency, getUnit).link();
106 }
107
108 /**
109 * Type of the callback used by [link] and [relink] to request
110 * [LinkedLibrary] objects from other build units.
111 */
112 typedef LinkedLibrary GetDependencyCallback(String absoluteUri);
113
114 /**
115 * Type of the callback used by [link[ and [relin] to request
scheglov 2016/03/25 18:30:27 mistypes
Paul Berry 2016/03/28 16:02:10 Done.
116 * [UnlinkedUnit] objects.
117 */
118 typedef UnlinkedUnit GetUnitCallback(String absoluteUri);
119
120 /**
121 * Element representing a class or enum resynthesized from a summary
122 * during linking.
123 */
124 abstract class ClassElementForLink
125 implements ClassElement, ReferenceableElementForLink {
126 @override
127 ConstructorElementForLink get asConstructor => unnamedConstructor;
128
129 /**
130 * Indicates whether this is the core class `Object`.
131 */
132 bool get isObject;
133
134 @override
135 String get name;
136
137 @override
138 ConstructorElementForLink get unnamedConstructor;
139
140 /**
141 * Perform type inference and cycle detection on this class and
142 * store the resulting information in the enclosing elements.
143 */
144 void link(LinkedUnitBuilder linkedUnit);
145
146 @override
147 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
148 }
149
150 /**
151 * Element representing a class resynthesized from a summary during
152 * linking.
153 */
154 class ClassElementForLink_Class extends ClassElementForLink {
155 /**
156 * The unlinked representation of the class in the summary.
157 */
158 final UnlinkedClass _unlinkedClass;
159
160 @override
161 final CompilationUnitElementForLink enclosingElement;
162
163 List<ConstructorElementForLink> _constructors;
164 ConstructorElementForLink _unnamedConstructor;
165 bool _unnamedConstructorComputed = false;
166 List<FieldElementForLink> _fields;
167 InterfaceTypeForLink _supertype;
168 InterfaceTypeForLink _type;
169
170 ClassElementForLink_Class(this._unlinkedClass, this.enclosingElement);
scheglov 2016/03/25 18:30:27 Maybe worth to keep the order of constructor param
Paul Berry 2016/03/28 16:02:10 Done.
171
172 @override
173 List<ConstructorElementForLink> get constructors {
174 if (_constructors == null) {
175 _constructors = <ConstructorElementForLink>[];
176 for (UnlinkedExecutable unlinkedExecutable
177 in _unlinkedClass.executables) {
178 if (unlinkedExecutable.kind == UnlinkedExecutableKind.constructor) {
179 _constructors
180 .add(new ConstructorElementForLink(unlinkedExecutable, this));
181 }
182 }
183 }
184 return _constructors;
185 }
186
187 @override
188 List<FieldElementForLink> get fields {
189 if (_fields == null) {
190 _fields = <FieldElementForLink>[];
191 for (UnlinkedVariable field in _unlinkedClass.fields) {
192 _fields.add(new FieldElementForLink(field, this));
193 }
194 }
195 return _fields;
196 }
197
198 @override
199 bool get isObject => _unlinkedClass.hasNoSupertype;
200
201 @override
202 String get name => _unlinkedClass.name;
203
204 @override
205 InterfaceTypeForLink get supertype {
206 if (isObject) {
207 return null;
208 }
209 return _supertype ??= _unlinkedClass.supertype == null
210 ? enclosingElement.enclosingElement._linker.objectType
211 : enclosingElement._resolveTypeRef(_unlinkedClass.supertype);
212 }
213
214 @override
215 ConstructorElementForLink get unnamedConstructor {
216 if (!_unnamedConstructorComputed) {
217 for (ConstructorElementForLink constructor in constructors) {
218 if (constructor.name.isEmpty) {
219 _unnamedConstructor = constructor;
220 break;
221 }
222 }
223 _unnamedConstructorComputed = true;
224 }
225 return _unnamedConstructor;
226 }
227
228 @override
229 DartTypeForLink buildType(DartTypeForLink getTypeArgument(int i),
230 List<int> implicitFunctionTypeIndices) {
231 if (_unlinkedClass.typeParameters.length != 0) {
232 // TODO(paulberry): implement.
233 throw new UnimplementedError();
234 } else {
235 return _type ??= new InterfaceTypeForLink(this);
236 }
237 }
238
239 @override
240 ReferenceableElementForLink getContainedName(name) {
241 // TODO(paulberry): implement.
242 throw new UnimplementedError();
243 }
244
245 @override
246 void link(LinkedUnitBuilder linkedUnit) {
247 for (ConstructorElementForLink constructorElement in constructors) {
248 constructorElement.link(linkedUnit);
249 }
250 }
251 }
252
253 /**
254 * Element representing an enum resynthesized from a summary during
255 * linking.
256 */
257 class ClassElementForLink_Enum extends ClassElementForLink {
258 /**
259 * The unlinked representation of the enum in the summary.
260 */
261 final UnlinkedEnum _unlinkedEnum;
262
263 ClassElementForLink_Enum(this._unlinkedEnum);
scheglov 2016/03/25 18:30:27 Do we need enclosingElement too?
Paul Berry 2016/03/28 16:02:10 I'm not sure. I'm working on a follow up CL, and
264
265 @override
266 bool get isObject => false;
267
268 @override
269 String get name => _unlinkedEnum.name;
270
271 @override
272 ConstructorElementForLink get unnamedConstructor => null;
273
274 @override
275 DartTypeForLink buildType(DartTypeForLink getTypeArgument(int i),
276 List<int> implicitFunctionTypeIndices) {
277 // TODO(paulberry): implement.
278 throw new UnimplementedError();
279 }
280
281 @override
282 ReferenceableElementForLink getContainedName(name) {
283 // TODO(paulberry): implement.
284 throw new UnimplementedError();
285 }
286
287 @override
288 void link(LinkedUnitBuilder linkedUnit) {}
289 }
290
291 /**
292 * Element representing a compilation unit resynthesized from a
293 * summary during linking.
294 */
295 abstract class CompilationUnitElementForLink implements CompilationUnitElement {
296 /**
297 * The unlinked representation of the compilation unit in the
298 * summary.
299 */
300 final UnlinkedUnit _unlinkedUnit;
301
302 /**
303 * For each entry in [UnlinkedUnit.references], the element referred
304 * to by the reference, or `null` if it hasn't been located yet.
305 */
306 final List<ReferenceableElementForLink> _references;
307
308 List<ClassElementForLink> _types;
309 Map<String, ReferenceableElementForLink> _containedNames;
310
311 @override
312 final LibraryElementForLink enclosingElement;
313
314 CompilationUnitElementForLink(
315 this.enclosingElement, UnlinkedUnit unlinkedUnit)
316 : _references = new List<ReferenceableElementForLink>(
317 unlinkedUnit.references.length),
318 _unlinkedUnit = unlinkedUnit;
319
320 @override
321 bool get isInBuildUnit;
322
323 @override
324 List<ClassElementForLink> get types {
325 if (_types == null) {
326 _types = <ClassElementForLink>[];
scheglov 2016/03/25 18:30:27 Do we want to create a fixed length list here too?
Paul Berry 2016/03/28 16:02:10 I'm not sure it's worth it--it's a fair amount of
327 for (UnlinkedClass unlinkedClass in _unlinkedUnit.classes) {
328 _types.add(new ClassElementForLink_Class(unlinkedClass, this));
329 }
330 for (UnlinkedEnum unlinkedEnum in _unlinkedUnit.enums) {
331 _types.add(new ClassElementForLink_Enum(unlinkedEnum));
332 }
333 }
334 return _types;
335 }
336
337 /**
338 * The linked representation of the compilation unit in the summary.
339 */
340 LinkedUnit get _linkedUnit;
341
342 /**
343 * Search the unit for a top level element with the given [name].
344 * If no name is found, return the singleton instance of
345 * [UndefinedElementForLink].
346 */
347 ReferenceableElementForLink getContainedName(name) {
348 if (_containedNames == null) {
349 _containedNames = <String, ReferenceableElementForLink>{};
350 for (ClassElementForLink type in types) {
351 // TODO(paulberry): what's the correct way to handle name conflicts?
352 _containedNames[type.name] = type;
353 }
354 // TODO(paulberry): fill in other top level entities.
355 }
356 return _containedNames.putIfAbsent(
357 name, () => UndefinedElementForLink.instance);
358 }
359
360 @override
361 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
362
363 /**
364 * Return the element referred to by the given [index] in
365 * [UnlinkedUnit.references]. If the reference is unresolved,
366 * return [UndefinedElementForLink.instance].
367 */
368 ReferenceableElementForLink _resolveRef(int index) {
369 if (_references[index] == null) {
370 UnlinkedReference unlinkedReference = _unlinkedUnit.references[index];
371 LinkedReference linkedReference = _linkedUnit.references[index];
372 String name = unlinkedReference.name;
373 int containingReference = unlinkedReference.prefixReference;
374 if (containingReference != 0) {
375 _references[index] =
376 _resolveRef(containingReference).getContainedName(name);
377 } else if (linkedReference.dependency == 0) {
378 _references[index] = enclosingElement.getContainedName(name);
379 } else {
380 // TODO(paulberry): implement.
381 throw new UnimplementedError();
382 }
383 }
384 return _references[index];
385 }
386
387 /**
388 * Resolve an [EntityRef] into a type. If the reference is
389 * unresolved, return [DynamicTypeImpl.instance].
390 *
391 * TODO(paulberry): or should we have a class representing an
392 * unresolved type, for consistency with the full element model?
393 */
394 DartTypeForLink _resolveTypeRef(EntityRef type, {bool defaultVoid: false}) {
395 if (type == null) {
396 if (defaultVoid) {
397 return VoidTypeForLink.instance;
398 } else {
399 return DynamicTypeForLink.instance;
400 }
401 }
402 if (type.paramReference != 0) {
403 // TODO(paulberry): implement.
404 throw new UnimplementedError();
405 } else if (type.syntheticReturnType != null) {
406 // TODO(paulberry): implement.
407 throw new UnimplementedError();
408 } else {
409 DartTypeForLink getTypeArgument(int i) {
410 if (i < type.typeArguments.length) {
411 return _resolveTypeRef(type.typeArguments[i]);
412 } else {
413 return DynamicTypeForLink.instance;
414 }
415 }
416 ReferenceableElementForLink element = _resolveRef(type.reference);
417 return element.buildType(
418 getTypeArgument, type.implicitFunctionTypeIndices);
419 }
420 }
421 }
422
423 /**
424 * Element representing a compilation unit which is part of the build
425 * unit being linked.
426 */
427 class CompilationUnitElementInBuildUnit extends CompilationUnitElementForLink {
428 @override
429 final LinkedUnitBuilder _linkedUnit;
430
431 CompilationUnitElementInBuildUnit(LibraryElementInBuildUnit libraryElement,
432 UnlinkedUnit unlinkedUnit, this._linkedUnit)
433 : super(libraryElement, unlinkedUnit);
434
435 @override
436 bool get isInBuildUnit => true;
437
438 /**
439 * Perform type inference and const cycle detection on this
440 * compilation unit.
441 */
442 void link() {
443 for (ClassElementForLink classElement in types) {
444 classElement.link(_linkedUnit);
445 }
446 }
447
448 /**
449 * Throw away any information produced by a previous call to [link].
450 */
451 void unlink() {
452 _linkedUnit.constCycles.clear();
453 _linkedUnit.references.length = _unlinkedUnit.references.length;
454 _linkedUnit.types.clear();
455 }
456 }
457
458 /**
459 * Element representing a compilation unit which is depended upon
460 * (either directly or indirectly) by the build unit being linked.
461 */
462 class CompilationUnitElementInDependency extends CompilationUnitElementForLink {
463 @override
464 final LinkedUnit _linkedUnit;
465
466 CompilationUnitElementInDependency(LibraryElementInDependency libraryElement,
467 UnlinkedUnit unlinkedUnit, this._linkedUnit)
468 : super(libraryElement, unlinkedUnit);
469
470 @override
471 bool get isInBuildUnit => false;
472 }
473
474 /**
475 * Instance of [ConstNode] representing a constant constructor.
476 */
477 class ConstConstructorNode extends ConstNode {
478 /**
479 * The [ConstructorElement] to which this node refers.
480 */
481 final ConstructorElementForLink constructorElement;
482
483 /**
484 * Once this node has been evaluated, indicates whether the
485 * constructor is free of constant evaluation cycles.
486 */
487 bool isCycleFree = false;
488
489 ConstConstructorNode(this.constructorElement);
490
491 @override
492 List<ConstNode> computeDependencies() {
493 List<ConstNode> dependencies = <ConstNode>[];
494 void safeAddDependency(ConstNode target) {
495 if (target != null) {
496 dependencies.add(target);
497 }
498 }
499 UnlinkedExecutable unlinkedExecutable =
500 constructorElement._unlinkedExecutable;
501 ClassElementForLink_Class enclosingClass =
502 constructorElement.enclosingElement;
503 ConstructorElementForLink redirectedConstructor =
504 _getConstRedirectedConstructor();
505 if (redirectedConstructor != null) {
506 if (redirectedConstructor._constNode != null) {
507 safeAddDependency(redirectedConstructor._constNode);
508 }
509 } else if (unlinkedExecutable.isFactory) {
510 // Factory constructor, but getConstRedirectedConstructor returned
511 // null. This can happen if we're visiting one of the special external
512 // const factory constructors in the SDK, or if the code contains
513 // errors (such as delegating to a non-const constructor, or delegating
514 // to a constructor that can't be resolved). In any of these cases,
515 // we'll evaluate calls to this constructor without having to refer to
516 // any other constants. So we don't need to report any dependencies.
517 } else {
518 bool superInvocationFound = false;
519 for (UnlinkedConstructorInitializer constructorInitializer
520 in constructorElement._unlinkedExecutable.constantInitializers) {
521 if (constructorInitializer.kind ==
522 UnlinkedConstructorInitializerKind.superInvocation) {
523 superInvocationFound = true;
524 }
525 CompilationUnitElementForLink compilationUnit =
526 constructorElement.enclosingElement.enclosingElement;
527 collectDependencies(
528 dependencies, constructorInitializer.expression, compilationUnit);
529 constructorInitializer.arguments.map((UnlinkedConst unlinkedConst) =>
530 collectDependencies(dependencies, unlinkedConst, compilationUnit));
531 }
532
533 if (!superInvocationFound) {
534 // No explicit superconstructor invocation found, so we need to
535 // manually insert a reference to the implicit superconstructor.
536 ClassElementForLink superClass = enclosingClass.supertype?.element;
537 if (superClass != null && !superClass.isObject) {
538 ConstructorElementForLink unnamedConstructor =
539 superClass.unnamedConstructor;
540 safeAddDependency(unnamedConstructor?._constNode);
541 }
542 }
543 for (FieldElementForLink field in enclosingClass.fields) {
544 // Note: non-static const isn't allowed but we handle it anyway so
545 // that we won't be confused by incorrect code.
546 if ((field.isFinal || field.isConst) && !field.isStatic) {
547 safeAddDependency(field._constNode);
548 }
549 }
550 for (ParameterElementForLink parameterElement
551 in constructorElement.parameters) {
552 safeAddDependency(parameterElement._constNode);
553 }
554 }
555 return dependencies;
556 }
557
558 /**
559 * If [constructorElement] redirects to another constructor, return
560 * the constructor it redirects to.
561 */
562 ConstructorElementForLink _getConstRedirectedConstructor() {
563 // TODO(paulberry): implement
564 return null;
565 }
566 }
567
568 /**
569 * Specialization of [DependencyWalker] for detecting constant
570 * evaluation cycles.
571 */
572 class ConstDependencyWalker extends DependencyWalker<ConstNode> {
573 @override
574 void evaluate(ConstNode v) {
575 if (v is ConstConstructorNode) {
576 v.isCycleFree = true;
577 }
578 v.isEvaluated = true;
579 }
580
581 @override
582 void evaluateScc(List<ConstNode> scc) {
583 for (ConstNode v in scc) {
584 if (v is ConstConstructorNode) {
585 v.isCycleFree = false;
586 }
587 v.isEvaluated = true;
588 }
589 }
590 }
591
592 /**
593 * Specialization of [Node] used to construct the constant evaluation
594 * dependency graph.
595 */
596 abstract class ConstNode extends Node<ConstNode> {
597 @override
598 bool isEvaluated = false;
599
600 /**
601 * Collect the dependencies in [unlinkedConst] (which should be
602 * interpreted relative to [compilationUnit]) and store them in
603 * [dependencies].
604 */
605 void collectDependencies(
606 List<ConstNode> dependencies,
607 UnlinkedConst unlinkedConst,
608 CompilationUnitElementForLink compilationUnit) {
609 if (unlinkedConst == null) {
610 return;
611 }
612 int refPtr = 0;
613 for (UnlinkedConstOperation operation in unlinkedConst.operations) {
614 switch (operation) {
615 case UnlinkedConstOperation.pushReference:
616 // TODO(paulberry): implement.
617 throw new UnimplementedError();
618 case UnlinkedConstOperation.makeTypedList:
619 refPtr++;
620 break;
621 case UnlinkedConstOperation.makeTypedMap:
622 refPtr += 2;
623 break;
624 case UnlinkedConstOperation.invokeConstructor:
625 EntityRef ref = unlinkedConst.references[refPtr++];
626 ConstructorElementForLink element =
627 compilationUnit._resolveRef(ref.reference).asConstructor;
628 if (element?._constNode != null) {
629 dependencies.add(element._constNode);
630 }
631 break;
632 default:
633 break;
634 }
635 }
636 assert(refPtr == unlinkedConst.references.length);
637 }
638 }
639
640 /**
641 * Instance of [ConstNode] representing a parameter with a default
642 * value.
643 */
644 class ConstParameterNode extends ConstNode {
645 /**
646 * The [ParameterElement] to which this node refers.
647 */
648 final ParameterElementForLink parameterElement;
649
650 ConstParameterNode(this.parameterElement);
651
652 @override
653 List<ConstNode> computeDependencies() {
654 List<ConstNode> dependencies = <ConstNode>[];
655 collectDependencies(
656 dependencies,
657 parameterElement._unlinkedParam.defaultValue,
658 parameterElement.compilationUnit);
659 return dependencies;
660 }
661 }
662
663 /**
664 * Element representing a constructor resynthesized from a summary
665 * during linking.
666 */
667 class ConstructorElementForLink implements ConstructorElement {
668 /**
669 * The unlinked representation of the constructor in the summary.
670 */
671 final UnlinkedExecutable _unlinkedExecutable;
672
673 /**
674 * If this is a `const` constructor and the enclosing library is
675 * part of the build unit being linked, the constructor's node in
676 * the constant evaluation dependency graph. Otherwise `null`.
677 */
678 ConstConstructorNode _constNode;
679
680 @override
681 final ClassElementForLink_Class enclosingElement;
682
683 List<ParameterElementForLink> _parameters;
684
685 ConstructorElementForLink(this._unlinkedExecutable, this.enclosingElement) {
686 if (enclosingElement.enclosingElement.isInBuildUnit &&
687 _unlinkedExecutable.constCycleSlot != 0) {
688 _constNode = new ConstConstructorNode(this);
689 }
690 }
691
692 @override
693 bool get isCycleFree {
694 if (!_constNode.isEvaluated) {
695 new ConstDependencyWalker().walk(_constNode);
696 }
697 return _constNode.isCycleFree;
698 }
699
700 @override
701 String get name => _unlinkedExecutable.name;
702
703 @override
704 List<ParameterElementForLink> get parameters {
705 if (_parameters == null) {
706 _parameters = <ParameterElementForLink>[];
707 for (UnlinkedParam unlinkedParam in _unlinkedExecutable.parameters) {
708 _parameters.add(new ParameterElementForLink(
709 unlinkedParam, enclosingElement.enclosingElement));
710 }
711 }
712 return _parameters;
713 }
714
715 /**
716 * Perform const cycle detection on this constructor.
717 */
718 void link(LinkedUnitBuilder linkedUnit) {
719 if (_constNode != null && !isCycleFree) {
720 linkedUnit.constCycles.add(_unlinkedExecutable.constCycleSlot);
721 }
722 }
723
724 @override
725 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
726 }
727
728 /**
729 * Instance of [ConstNode] representing a constant field or constant
730 * top level variable.
731 */
732 class ConstVariableNode extends ConstNode {
733 /**
734 * The [FieldElement] or [TopLevelVariableElement] to which this
735 * node refers.
736 */
737 final VariableElementForLink variableElement;
738
739 ConstVariableNode(this.variableElement);
740
741 @override
742 List<ConstNode> computeDependencies() {
743 List<ConstNode> dependencies = <ConstNode>[];
744 collectDependencies(
745 dependencies,
746 variableElement.unlinkedVariable.constExpr,
747 variableElement.compilationUnit);
748 return dependencies;
749 }
750 }
751
752 /**
753 * Representation of a type resynthesized from a summary during linking.
754 */
755 class DartTypeForLink implements DartType {
756 const DartTypeForLink();
757
758 @override
759 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
760 }
761
762 /**
763 * An instance of [DependencyWalker] contains the core algorithms for
764 * walking a dependency graph and evaluating nodes in a safe order.
765 */
766 abstract class DependencyWalker<NodeType extends Node<NodeType>> {
767 /**
768 * Called by [walk] to evaluate a single non-cyclical node, after
769 * all that node's dependencies have been evaluated.
770 */
771 void evaluate(NodeType v);
772
773 /**
774 * Called by [walk] to evaluate a strongly connected component
775 * containing one or more nodes. All dependencies of the strongly
776 * connected component have been evaluated.
777 */
778 void evaluateScc(List<NodeType> scc);
779
780 /**
781 * Walk the dependency graph starting at [startingPoint], finding
782 * strongly connected components and evaluating them in a safe order
783 * by calling [evaluate] and [evaluateScc].
784 *
785 * This is an implementation of Tarjan's strongly connected
786 * components algorithm
787 * (https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_alg orithm).
788 */
789 void walk(NodeType startingPoint) {
790 // TODO(paulberry): consider rewriting in a non-recursive way so
791 // that long dependency chains don't cause stack overflow.
792
793 // TODO(paulberry): in the event that an exception occurs during
794 // the walk, restore the state of the [Node] data structures so
795 // that further evaluation will be safe.
796
797 // The index which will be assigned to the next node that is
798 // freshly visited.
799 int index = 1;
800
801 // Stack of nodes which have been seen so far and whose strongly
802 // connected component is still being determined. Nodes are only
803 // popped off the stack when they are evaluated, so sometimes the
804 // stack contains nodes that were visited after the current node.
805 List<NodeType> stack = <NodeType>[];
806
807 void strongConnect(NodeType node) {
808 // Assign the current node an index and add it to the stack. We
809 // haven't seen any of its dependencies yet, so set its lowLink
810 // to its index, indicating that so far it is the only node in
811 // its strongly connected component.
812 node.index = node.lowLink = index++;
813 stack.add(node);
814
815 // Consider the node's dependencies one at a time.
816 for (NodeType dependency in node.dependencies) {
817 // If the dependency has already been evaluated, it can't be
818 // part of this node's strongly connected component, so we can
819 // skip it.
820 if (dependency.isEvaluated) {
821 continue;
822 }
823 if (dependency.index == 0) {
824 // The dependency hasn't been seen yet, so recurse on it.
825 strongConnect(dependency);
826 // If the dependency's lowLink refers to a node that was
827 // visited before the current node, that means that the
828 // current node, the dependency, and the node referred to by
829 // the dependency's lowLink are all part of the same
830 // strongly connected component, so we need to update the
831 // current node's lowLink accordingly.
832 if (dependency.lowLink < node.lowLink) {
833 node.lowLink = dependency.lowLink;
834 }
835 } else {
836 // The dependency has already been seen, so it is part of
837 // the current node's strongly connected component. If it
838 // was visited earlier than the current node's lowLink, then
839 // it is a new addition to the current node's strongly
840 // connected component, so we need to update the current
841 // node's lowLink accordingly.
842 if (dependency.index < node.lowLink) {
843 node.lowLink = dependency.index;
844 }
845 }
846 }
847
848 // If the current node's lowLink is the same as its index, then
849 // we have finished visiting a strongly connected component, so
850 // pop the stack and evaluate it before moving on.
851 if (node.lowLink == node.index) {
852 // In the case where the strongly connected component has only
853 // one node, determine whether there is a trivial cycle or
854 // not.
855 //
856 // TODO(paulberry): could we figure this out in the for-loop
857 // above and save some effort?
858 if (identical(stack.last, node)) {
859 stack.removeLast();
860 if (_hasTrivialScc(node)) {
861 evaluateScc(<NodeType>[node]);
862 } else {
863 evaluate(node);
864 }
865 } else {
866 // There are multiple nodes in the strongly connected
867 // component.
868 List<NodeType> scc = <NodeType>[];
869 while (true) {
870 NodeType otherNode = stack.removeLast();
871 scc.add(otherNode);
872 if (identical(otherNode, node)) {
873 break;
874 }
875 }
876 evaluateScc(scc);
877 }
878 }
879 }
880
881 // Kick off the algorithm starting with the starting point.
882 strongConnect(startingPoint);
883 }
884
885 /**
886 * The given [node] is in a strongly connected component of size 1.
887 * Determine if it contains a trivial cycle (i.e. depends on
888 * itself).
889 */
890 bool _hasTrivialScc(NodeType node) {
891 for (NodeType dependency in node.dependencies) {
892 if (identical(dependency, node)) {
893 return true;
894 }
895 }
896 return false;
897 }
898 }
899
900 /**
901 * Representation of the dynamic type during linking.
902 */
903 class DynamicTypeForLink extends DartTypeForLink {
904 /**
905 * Singleton instance of the dynamic type.
906 */
907 static const DynamicTypeForLink instance = const DynamicTypeForLink._();
908
909 const DynamicTypeForLink._();
910 }
911
912 /**
913 * Element representing a field resynthesized from a summary during linking.
914 */
915 class FieldElementForLink extends VariableElementForLink
916 implements FieldElement {
917 /**
918 * The unlinked representation of the field in the summary.
919 */
920 final ClassElementForLink_Class enclosingElement;
921
922 FieldElementForLink(UnlinkedVariable unlinkedVariable,
923 ClassElementForLink_Class enclosingElement)
924 : enclosingElement = enclosingElement,
925 super(unlinkedVariable, enclosingElement.enclosingElement);
926
927 @override
928 bool get isConst => unlinkedVariable.isConst;
929
930 @override
931 bool get isFinal => unlinkedVariable.isFinal;
932
933 @override
934 bool get isStatic => unlinkedVariable.isStatic;
935
936 @override
937 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
938 }
939
940 /**
941 * Representation of an interface type during linking.
942 */
943 class InterfaceTypeForLink extends DartTypeForLink implements InterfaceType {
944 @override
945 final ClassElementForLink element;
946
947 InterfaceTypeForLink(this.element);
948 }
949
950 /**
951 * Element representing a library resynthesied from a summary during
952 * linking. The type parameter, [UnitElement], represents the type
953 * that will be used for the compilation unit elements.
954 */
955 abstract class LibraryElementForLink<
956 UnitElement extends CompilationUnitElementForLink>
957 implements LibraryElement {
958 /**
959 * Pointer back to the linker.
960 */
961 final _Linker _linker;
962
963 /**
964 * The absolute URI of this library.
965 */
966 final Uri _absoluteUri;
967
968 List<UnitElement> _units;
969 final Map<String, ReferenceableElementForLink> _containedNames =
970 <String, ReferenceableElementForLink>{};
971
972 LibraryElementForLink(this._linker, this._absoluteUri);
973
974 @override
975 List<UnitElement> get units {
976 if (_units == null) {
977 UnlinkedUnit definingUnit = _linker.getUnit(_absoluteUri.toString());
978 _units = <UnitElement>[_makeUnitElement(definingUnit, 0)];
979 int numParts = definingUnit.parts.length;
980 for (int i = 0; i < numParts; i++) {
981 // TODO(paulberry): make sure we handle the case where Uri.parse fails.
982 // TODO(paulberry): make sure we handle the case where
983 // resolveRelativeUri fails.
984 UnlinkedUnit partUnit = _linker.getUnit(resolveRelativeUri(
985 _absoluteUri, Uri.parse(definingUnit.publicNamespace.parts[i]))
986 .toString());
987 _units.add(
988 _makeUnitElement(partUnit ?? new UnlinkedUnitBuilder(), i + 1));
989 }
990 }
991 return _units;
992 }
993
994 /**
995 * The linked representation of the library in the summary.
996 */
997 LinkedLibrary get _linkedLibrary;
998
999 /**
1000 * Search all the units for a top level element with the given
1001 * [name]. If no name is found, return the singleton instance of
1002 * [UndefinedElementForLink].
1003 */
1004 ReferenceableElementForLink getContainedName(name) =>
1005 _containedNames.putIfAbsent(name, () {
1006 for (UnitElement unit in units) {
1007 ReferenceableElementForLink element = unit.getContainedName(name);
1008 if (!identical(element, UndefinedElementForLink.instance)) {
1009 return element;
1010 }
1011 }
1012 return UndefinedElementForLink.instance;
1013 });
1014
1015 @override
1016 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
1017
1018 /**
1019 * Create a [UnitElement] for one of the library's compilation
1020 * units.
1021 */
1022 UnitElement _makeUnitElement(UnlinkedUnit unlinkedUnit, int i);
1023 }
1024
1025 /**
1026 * Element representing a library which is part of the build unit
1027 * being linked.
1028 */
1029 class LibraryElementInBuildUnit
1030 extends LibraryElementForLink<CompilationUnitElementInBuildUnit> {
1031 @override
1032 final LinkedLibraryBuilder _linkedLibrary;
1033
1034 LibraryElementInBuildUnit(
1035 _Linker linker, Uri absoluteUri, this._linkedLibrary)
1036 : super(linker, absoluteUri);
1037
1038 /**
1039 * Perform type inference and const cycle detection on this library.
1040 */
1041 void link() {
1042 for (CompilationUnitElementInBuildUnit unit in units) {
1043 unit.link();
1044 }
1045 }
1046
1047 /**
1048 * Throw away any information produced by a previous call to [link].
1049 */
1050 void unlink() {
1051 _linkedLibrary.dependencies.length =
1052 _linkedLibrary.numPrelinkedDependencies;
1053 for (CompilationUnitElementInBuildUnit unit in units) {
1054 unit.link();
1055 }
1056 }
1057
1058 @override
1059 CompilationUnitElementInBuildUnit _makeUnitElement(
1060 UnlinkedUnit unlinkedUnit, int i) =>
1061 new CompilationUnitElementInBuildUnit(
1062 this, unlinkedUnit, _linkedLibrary.units[i]);
1063 }
1064
1065 /**
1066 * Element representing a library which is depended upon (either
1067 * directly or indirectly) by the build unit being linked.
1068 */
1069 class LibraryElementInDependency
1070 extends LibraryElementForLink<CompilationUnitElementInDependency> {
1071 @override
1072 final LinkedLibrary _linkedLibrary;
1073
1074 LibraryElementInDependency(
1075 _Linker linker, Uri absoluteUri, this._linkedLibrary)
1076 : super(linker, absoluteUri);
1077
1078 @override
1079 CompilationUnitElementInDependency _makeUnitElement(
1080 UnlinkedUnit unlinkedUnit, int i) =>
1081 new CompilationUnitElementInDependency(
1082 this, unlinkedUnit, _linkedLibrary.units[i]);
1083 }
1084
1085 /**
1086 * Instances of [Node] represent nodes in a dependency graph. The
1087 * type parameter, [NodeType], is the derived type (this affords some
1088 * extra type safety by making it difficult to accidentally construct
1089 * bridges between unrelated dependency graphs).
1090 */
1091 abstract class Node<NodeType> {
1092 /**
1093 * Index used by Tarjan's strongly connected components algorithm.
1094 * Zero means the node has not been visited yet; a nonzero value
1095 * counts the order in which the node was visited.
1096 */
1097 int index = 0;
1098
1099 /**
1100 * Low link used by Tarjan's strongly connected components
1101 * algorithm. This represents the smallest [index] of all the nodes
1102 * in the strongly connected component to which this node belongs.
1103 */
1104 int lowLink = 0;
1105
1106 List<NodeType> _dependencies;
1107
1108 /**
1109 * Retrieve the dependencies of this node.
1110 */
1111 List<NodeType> get dependencies => _dependencies ??= computeDependencies();
1112
1113 /**
1114 * Indicates whether this node has been evaluated yet.
1115 */
1116 bool get isEvaluated;
1117
1118 /**
1119 * Compute the dependencies of this node.
1120 */
1121 List<NodeType> computeDependencies();
1122 }
1123
1124 /**
1125 * Element representing a function or method parameter resynthesized
1126 * from a summary during linking.
1127 */
1128 class ParameterElementForLink implements ParameterElement {
1129 /**
1130 * The unlinked representation of the parameter in the summary.
1131 */
1132 final UnlinkedParam _unlinkedParam;
1133
1134 /**
1135 * If this parameter has a default value and the enclosing library
1136 * is part of the build unit being linked, the parameter's node in
1137 * the constant evaluation dependency graph. Otherwise `null`.
1138 */
1139 ConstNode _constNode;
1140
1141 /**
1142 * The compilation unit in which this parameter appears.
1143 */
1144 final CompilationUnitElementForLink compilationUnit;
1145
1146 ParameterElementForLink(this._unlinkedParam, this.compilationUnit) {
1147 if (_unlinkedParam.defaultValue != null) {
1148 _constNode = new ConstParameterNode(this);
1149 }
1150 }
1151
1152 @override
1153 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
1154 }
1155
1156 /**
1157 * Abstract base class representing an element which can be the target
1158 * of a reference.
1159 */
1160 abstract class ReferenceableElementForLink {
1161 /**
1162 * If this element can be used in a constructor invocation context,
1163 * return the associated constructor (which may be `this` or some
1164 * other element). Otherwise return `null`.
1165 */
1166 ConstructorElementForLink get asConstructor;
1167
1168 /**
1169 * Return the type indicated by this element when it is used in a
1170 * type instantiation context. If this element can't legally be
1171 * instantiated as a type, return the dynamic type.
1172 */
1173 DartTypeForLink buildType(DartTypeForLink getTypeArgument(int i),
1174 List<int> implicitFunctionTypeIndices);
1175
1176 /**
1177 * If this element contains other named elements, return the
1178 * contained element having the given [name]. If this element can't
1179 * contain other named elements, or it doesn't contain an element
1180 * with the given name, return the singleton of
1181 * [UndefinedElementForLink].
1182 */
1183 ReferenceableElementForLink getContainedName(name);
1184 }
1185
1186 /**
1187 * Singleton element used for unresolved references.
1188 */
1189 class UndefinedElementForLink implements ReferenceableElementForLink {
1190 static const UndefinedElementForLink instance =
1191 const UndefinedElementForLink._();
1192
1193 const UndefinedElementForLink._();
1194
1195 @override
1196 ConstructorElementForLink get asConstructor => null;
1197
1198 @override
1199 DartTypeForLink buildType(DartTypeForLink getTypeArgument(int i),
1200 List<int> implicitFunctionTypeIndices) =>
1201 DynamicTypeForLink.instance;
1202
1203 @override
1204 ReferenceableElementForLink getContainedName(name) => this;
1205 }
1206
1207 /**
1208 * Element representing a top level variable resynthesized from a
1209 * summary during linking.
1210 */
1211 class VariableElementForLink {
1212 /**
1213 * The unlinked representation of the variable in the summary.
1214 */
1215 final UnlinkedVariable unlinkedVariable;
1216
1217 /**
1218 * If this variable is declared `const` and the enclosing library is
1219 * part of the build unit being linked, the variable's node in the
1220 * constant evaluation dependency graph. Otherwise `null`.
1221 */
1222 ConstNode _constNode;
1223
1224 /**
1225 * The compilation unit in which this variable appears.
1226 */
1227 final CompilationUnitElementForLink compilationUnit;
1228
1229 VariableElementForLink(this.unlinkedVariable, this.compilationUnit) {
1230 if (compilationUnit.isInBuildUnit && unlinkedVariable.constExpr != null) {
1231 _constNode = new ConstVariableNode(this);
1232 }
1233 }
1234 }
1235
1236 /**
1237 * Representation of the void type during linking.
1238 */
1239 class VoidTypeForLink extends DartTypeForLink {
1240 static const VoidTypeForLink instance = const VoidTypeForLink._();
1241 const VoidTypeForLink._();
1242 }
1243
1244 /**
1245 * Instances of [_Linker] contain the necessary information to link
1246 * together a single build unit.
1247 */
1248 class _Linker {
1249 /**
1250 * Callback to ask the client for a [LinkedLibrary] for a
1251 * dependency.
1252 */
1253 final GetDependencyCallback getDependency;
1254
1255 /**
1256 * Callback to ask the client for an [UnlinkedUnit].
1257 */
1258 final GetUnitCallback getUnit;
1259
1260 /**
1261 * Map containing all library elements accessed during linking,
1262 * whether they are part of the build unit being linked or whether
1263 * they are dependencies.
1264 */
1265 final Map<Uri, LibraryElementForLink> _libraries =
1266 <Uri, LibraryElementForLink>{};
1267
1268 /**
1269 * List of library elements for the libraries in the build unit
1270 * being linked.
1271 */
1272 final List<LibraryElementInBuildUnit> _librariesInBuildUnit =
1273 <LibraryElementInBuildUnit>[];
1274
1275 InterfaceTypeForLink _objectType;
1276 LibraryElementForLink _coreLibrary;
1277
1278 _Linker(Map<String, LinkedLibraryBuilder> linkedLibraries, this.getDependency,
1279 this.getUnit) {
1280 // Create elements for the libraries to be linked. The rest of
1281 // the element model will be created on demand.
1282 linkedLibraries
1283 .forEach((String absoluteUri, LinkedLibraryBuilder linkedLibrary) {
1284 Uri uri = Uri.parse(absoluteUri);
1285 _librariesInBuildUnit.add(_libraries[uri] =
1286 new LibraryElementInBuildUnit(this, uri, linkedLibrary));
1287 });
1288 }
1289
1290 /**
1291 * Get the library element for `dart:core`.
1292 */
1293 LibraryElementForLink get coreLibrary =>
1294 _coreLibrary ??= getLibrary(Uri.parse('dart:core'));
1295
1296 /**
1297 * Get the `InterfaceType` for the type `Object`.
1298 */
1299 InterfaceTypeForLink get objectType => _objectType ??= coreLibrary
1300 .getContainedName('Object')
1301 .buildType((int i) => DynamicTypeForLink.instance, const []);
1302
1303 /**
1304 * Get the library element for the library having the given [uri].
1305 */
1306 LibraryElementForLink getLibrary(Uri uri) => _libraries.putIfAbsent(
1307 uri,
1308 () => new LibraryElementInDependency(
1309 this, uri, getDependency(uri.toString())));
1310
1311 /**
1312 * Perform type inference and const cycle detection on all libraries
1313 * in the build unit being linked.
1314 */
1315 void link() {
1316 for (LibraryElementInBuildUnit library in _librariesInBuildUnit) {
1317 library.link();
1318 }
1319 // TODO(paulberry): set dependencies.
1320 }
1321
1322 /**
1323 * Throw away any information produced by a previous call to [link].
1324 */
1325 void unlink() {
1326 for (LibraryElementInBuildUnit library in _librariesInBuildUnit) {
1327 library.unlink();
1328 }
1329 }
1330 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/summary/prelink.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698