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

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

Issue 1735243003: Initial package indexing implementation. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Fixes for review comments. 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/test/src/abstract_single_unit.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 import 'package:analyzer/dart/ast/ast.dart';
6 import 'package:analyzer/dart/ast/token.dart';
7 import 'package:analyzer/dart/ast/visitor.dart';
8 import 'package:analyzer/dart/element/element.dart';
9 import 'package:analyzer/dart/element/type.dart';
10 import 'package:analyzer/src/generated/utilities_dart.dart';
11 import 'package:analyzer/src/summary/format.dart';
12 import 'package:analyzer/src/summary/idl.dart';
13
14 /**
15 * Object that gathers information about the whole package index and then uses
16 * it to assemble a new [PackageIndexBuilder]. Call [index] on each compilation
17 * unit to be indexed, then call [assemble] to retrieve the complete index for
18 * the package.
19 */
20 class PackageIndexAssembler {
21 /**
22 * Map associating referenced elements with their [_ElementInfo]s.
23 */
24 final Map<Element, _ElementInfo> _elementMap = <Element, _ElementInfo>{};
25
26 /**
27 * Map associating [CompilationUnitElement]s with their identifiers, which
28 * are indices into [_elementLibraryUris] and [_elementUnitUris].
29 */
30 final Map<CompilationUnitElement, int> _elementUnitMap =
31 <CompilationUnitElement, int>{};
32
33 /**
34 * Each item of this list corresponds to the library URI of a unique
35 * [CompilationUnitElement]. It is an index into [_uris].
36 */
37 final List<int> _elementLibraryUris = <int>[];
38
39 /**
40 * Each item of this list corresponds to the unit URI of a unique
41 * [CompilationUnitElement]. It is an index into [_uris].
42 */
43 final List<int> _elementUnitUris = <int>[];
44
45 /**
46 * Map associating URIs with their identifiers, which are indices
47 * into [_uris].
48 */
49 final Map<String, int> _uriMap = <String, int>{};
50
51 /**
52 * List of unique URIs used in this index.
53 */
54 final List<String> _uris = <String>[];
55
56 /**
57 * List of information about each unit indexed in this index.
58 */
59 final List<_UnitIndexAssembler> _units = <_UnitIndexAssembler>[];
60
61 /**
62 * Assemble a new [PackageIndexBuilder] using the information gathered by
63 * [index].
64 */
65 PackageIndexBuilder assemble() {
66 List<_ElementInfo> elementInfoList = _elementMap.values.toList();
67 elementInfoList.sort((a, b) {
68 return a.offset - b.offset;
69 });
70 for (int i = 0; i < elementInfoList.length; i++) {
71 elementInfoList[i].id = i;
72 }
73 return new PackageIndexBuilder(
74 elementLibraryUris: _elementLibraryUris,
75 elementUnitUris: _elementUnitUris,
76 elementUnits: elementInfoList.map((e) => e.unitId).toList(),
77 elementOffsets: elementInfoList.map((e) => e.offset).toList(),
78 uris: _uris,
79 units: _units.map((unit) => unit.assemble()).toList());
80 }
81
82 /**
83 * Index the given fully resolved [unit].
84 */
85 void index(CompilationUnit unit) {
86 CompilationUnitElement unitElement = unit.element;
87 _UnitIndexAssembler assembler = new _UnitIndexAssembler(this, unitElement);
88 _units.add(assembler);
89 unit.accept(new _IndexContributor(assembler));
90 }
91
92 /**
93 * Return the unique [_ElementInfo] corresponding the [element]. The field
94 * [_ElementInfo.id] is filled by [assemble] during final sorting.
95 */
96 _ElementInfo _getElementInfo(Element element) {
97 return _elementMap.putIfAbsent(element, () {
98 CompilationUnitElement unitElement = getUnitElement(element);
99 int unitId = _getUnitElementId(unitElement);
100 return new _ElementInfo(unitId, element.nameOffset);
101 });
102 }
103
104 /**
105 * Add information about [unitElement] to [_elementUnitUris] and
106 * [_elementLibraryUris] if necessary, and return the location in those
107 * arrays representing [unitElement].
108 */
109 int _getUnitElementId(CompilationUnitElement unitElement) {
110 return _elementUnitMap.putIfAbsent(unitElement, () {
111 assert(_elementLibraryUris.length == _elementUnitUris.length);
112 int id = _elementUnitUris.length;
113 _elementLibraryUris.add(_getUriId(unitElement.library.source.uri));
114 _elementUnitUris.add(_getUriId(unitElement.source.uri));
115 return id;
116 });
117 }
118
119 /**
120 * Add information about [uri] to [_uris] if necessary, and return the
121 * location in this array representing [uri].
122 */
123 int _getUriId(Uri uri) {
124 String str = uri.toString();
125 return _uriMap.putIfAbsent(str, () {
126 int id = _uris.length;
127 _uris.add(str);
128 return id;
129 });
130 }
131
132 /**
133 * Return the [CompilationUnitElement] that should be used for [element].
134 * Throw [StateError] if the [element] is not linked into a unit.
135 */
136 static CompilationUnitElement getUnitElement(Element element) {
137 for (Element e = element; e != null; e = e.enclosingElement) {
138 if (e is CompilationUnitElement) {
139 return e;
140 }
141 if (e is LibraryElement) {
142 return e.definingCompilationUnit;
143 }
144 }
145 throw new StateError(element.toString());
146 }
147 }
148
149 /**
150 * Information about an element referenced in index.
151 */
152 class _ElementInfo {
153 /**
154 * The identifier of the [CompilationUnitElement] containing this element.
155 */
156 final int unitId;
157
158 /**
159 * The name offset of the element.
160 */
161 final int offset;
162
163 /**
164 * The unique id of the element. It is set after indexing of the whole
165 * package is done and we are assembling the full package index.
166 */
167 int id;
168
169 _ElementInfo(this.unitId, this.offset);
170 }
171
172 /**
173 * Visits a resolved AST and adds relationships into [_UnitIndexAssembler].
174 */
175 class _IndexContributor extends GeneralizingAstVisitor {
176 final _UnitIndexAssembler assembler;
177
178 _IndexContributor(this.assembler);
179
180 /**
181 * Record information about a [ClassDeclaration] or [ClassTypeAlias] with
182 * the given [nameNode]. Nodes [superNode], [withClause] and
183 * [implementsClause] can be `null`.
184 */
185 void recordClassClauses(SimpleIdentifier nameNode, TypeName superNode,
186 WithClause withClause, ImplementsClause implementsClause) {
187 if (superNode != null) {
188 recordSuperType(superNode, IndexRelationKind.IS_EXTENDED_BY);
189 } else {
190 ClassElement element = nameNode.staticElement;
191 InterfaceType superType = element.supertype;
192 if (superType != null) {
193 ClassElement objectElement = superType.element;
194 recordRelationOffset(objectElement, IndexRelationKind.IS_EXTENDED_BY,
195 nameNode.offset, 0);
196 }
197 }
198 if (withClause != null) {
199 for (TypeName mixinNode in withClause.mixinTypes) {
200 recordSuperType(mixinNode, IndexRelationKind.IS_MIXED_IN_BY);
201 }
202 }
203 if (implementsClause != null) {
204 for (TypeName interfaceNode in implementsClause.interfaces) {
205 recordSuperType(interfaceNode, IndexRelationKind.IS_IMPLEMENTED_BY);
206 }
207 }
208 }
209
210 /**
211 * Records reference to defining [CompilationUnitElement] of the given
212 * [LibraryElement].
213 */
214 void recordLibraryReference(UriBasedDirective node, LibraryElement library) {
215 recordRelation(library, IndexRelationKind.IS_REFERENCED_BY, node?.uri);
216 }
217
218 /**
219 * Record reference to the given operator [Element] and name.
220 */
221 void recordOperatorReference(Token operator, Element element) {
222 recordRelationToken(element, IndexRelationKind.IS_INVOKED_BY, operator);
223 // TODO(scheglov) do we need this?
224 // // prepare location
225 // LocationImpl location = _createLocationForToken(operator, element != null) ;
226 // // record name reference
227 // {
228 // String name = operator.lexeme;
229 // if (name == "++") {
230 // name = "+";
231 // }
232 // if (name == "--") {
233 // name = "-";
234 // }
235 // if (StringUtilities.endsWithChar(name, 0x3D) && name != "==") {
236 // name = name.substring(0, name.length - 1);
237 // }
238 // IndexableName indexableName = new IndexableName(name);
239 // recordRelationshipIndexable(
240 // indexableName, IndexConstants.IS_INVOKED_BY, location);
241 // }
242 // // record element reference
243 // if (element != null) {
244 // recordRelationshipElement(
245 // element, IndexConstants.IS_INVOKED_BY, location);
246 // }
247 }
248
249 /**
250 * Record that [element] has a relation of the given [kind] at the location
251 * of the given [node].
252 */
253 void recordRelation(Element element, IndexRelationKind kind, AstNode node) {
254 if (element != null && node != null) {
255 recordRelationOffset(element, kind, node.offset, node.length);
256 }
257 }
258
259 /**
260 * Record that [element] has a relation of the given [kind] at the given
261 * [offset] and [length].
262 */
263 void recordRelationOffset(
264 Element element, IndexRelationKind kind, int offset, int length) {
265 // Ignore elements that can't be referenced outside of the unit.
266 if (element == null ||
267 element is LocalVariableElement ||
268 element is ParameterElement &&
269 element.parameterKind != ParameterKind.NAMED ||
270 element is FunctionElement &&
271 element.enclosingElement is ExecutableElement) {
272 return;
273 }
274 // Add the relation.
275 assembler.addRelation(element, kind, offset, length);
276 }
277
278 /**
279 * Record that [element] has a relation of the given [kind] at the location
280 * of the given [token].
281 */
282 void recordRelationToken(
283 Element element, IndexRelationKind kind, Token token) {
284 if (element != null && token != null) {
285 recordRelationOffset(element, kind, token.offset, token.length);
286 }
287 }
288
289 /**
290 * Records a relation between [superNode] and its [Element].
291 */
292 void recordSuperType(TypeName superNode, IndexRelationKind kind) {
293 if (superNode != null) {
294 Identifier superName = superNode.name;
295 if (superName != null) {
296 Element superElement = superName.staticElement;
297 recordRelation(superElement, kind, superName);
298 }
299 }
300 }
301
302 /**
303 * Record the top-level [element] definition.
304 */
305 void recordTopLevelElementDefinition(Element element) {
306 // TODO(scheglov) do we need this?
307 // if (element?.enclosingElement is CompilationUnitElement) {
308 // IndexableElement indexable = new IndexableElement(element);
309 // int offset = element.nameOffset;
310 // int length = element.nameLength;
311 // LocationImpl location = new LocationImpl(indexable, offset, length);
312 // recordRelationshipElement(
313 // _libraryElement, IndexConstants.DEFINES, location);
314 // _store.recordTopLevelDeclaration(element);
315 // }
316 }
317
318 void recordUriFileReference(UriBasedDirective directive) {
319 Element element = directive.element;
320 recordRelation(element, IndexRelationKind.IS_REFERENCED_BY, directive.uri);
321 }
322
323 @override
324 visitAssignmentExpression(AssignmentExpression node) {
325 recordOperatorReference(node.operator, node.bestElement);
326 super.visitAssignmentExpression(node);
327 }
328
329 @override
330 visitBinaryExpression(BinaryExpression node) {
331 recordOperatorReference(node.operator, node.bestElement);
332 super.visitBinaryExpression(node);
333 }
334
335 @override
336 visitClassDeclaration(ClassDeclaration node) {
337 ClassElement element = node.element;
338 recordTopLevelElementDefinition(element);
339 recordClassClauses(node.name, node.extendsClause?.superclass,
340 node.withClause, node.implementsClause);
341 super.visitClassDeclaration(node);
342 }
343
344 @override
345 visitClassTypeAlias(ClassTypeAlias node) {
346 ClassElement element = node.element;
347 recordTopLevelElementDefinition(element);
348 recordClassClauses(
349 node.name, node.superclass, node.withClause, node.implementsClause);
350 super.visitClassTypeAlias(node);
351 }
352
353 @override
354 visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
355 SimpleIdentifier fieldName = node.fieldName;
356 if (fieldName != null) {
357 Element element = fieldName.staticElement;
358 recordRelation(element, IndexRelationKind.IS_REFERENCED_BY, fieldName);
359 }
360 node.expression?.accept(this);
361 }
362
363 @override
364 visitConstructorName(ConstructorName node) {
365 ConstructorElement element = node.staticElement;
366 // in 'class B = A;' actually A constructors are invoked
367 // TODO(scheglov) add support for multiple levels of redirection
368 // TODO(scheglov) test for a loop of redirection
369 if (element != null &&
370 element.isSynthetic &&
371 element.redirectedConstructor != null) {
372 element = element.redirectedConstructor;
373 }
374 // record relation
375 if (node.name != null) {
376 int offset = node.period.offset;
377 int length = node.name.end - offset;
378 recordRelationOffset(
379 element, IndexRelationKind.IS_REFERENCED_BY, offset, length);
380 } else {
381 int offset = node.type.end;
382 recordRelationOffset(
383 element, IndexRelationKind.IS_REFERENCED_BY, offset, 0);
384 }
385 super.visitConstructorName(node);
386 }
387
388 @override
389 visitEnumDeclaration(EnumDeclaration node) {
390 ClassElement element = node.element;
391 recordTopLevelElementDefinition(element);
392 super.visitEnumDeclaration(node);
393 }
394
395 @override
396 visitExportDirective(ExportDirective node) {
397 ExportElement element = node.element;
398 if (element != null) {
399 LibraryElement expLibrary = element.exportedLibrary;
400 recordLibraryReference(node, expLibrary);
401 }
402 recordUriFileReference(node);
403 super.visitExportDirective(node);
404 }
405
406 @override
407 visitFunctionDeclaration(FunctionDeclaration node) {
408 Element element = node.element;
409 recordTopLevelElementDefinition(element);
410 super.visitFunctionDeclaration(node);
411 }
412
413 @override
414 visitFunctionTypeAlias(FunctionTypeAlias node) {
415 Element element = node.element;
416 recordTopLevelElementDefinition(element);
417 super.visitFunctionTypeAlias(node);
418 }
419
420 @override
421 visitImportDirective(ImportDirective node) {
422 ImportElement element = node.element;
423 if (element != null) {
424 LibraryElement impLibrary = element.importedLibrary;
425 recordLibraryReference(node, impLibrary);
426 }
427 recordUriFileReference(node);
428 super.visitImportDirective(node);
429 }
430
431 @override
432 visitIndexExpression(IndexExpression node) {
433 MethodElement element = node.bestElement;
434 if (element is MethodElement) {
435 Token operator = node.leftBracket;
436 recordRelationToken(element, IndexRelationKind.IS_INVOKED_BY, operator);
437 }
438 super.visitIndexExpression(node);
439 }
440
441 @override
442 visitMethodInvocation(MethodInvocation node) {
443 SimpleIdentifier name = node.methodName;
444 // TODO(scheglov) do we need this?
445 // LocationImpl location = _createLocationForNode(name);
446 // // name invocation
447 // recordRelationshipIndexable(
448 // new IndexableName(name.name), IndexConstants.IS_INVOKED_BY, location);
449 // element invocation
450 Element element = name.bestElement;
451 if (element is MethodElement ||
452 element is PropertyAccessorElement ||
453 element is FunctionElement ||
454 element is VariableElement) {
455 recordRelation(element, IndexRelationKind.IS_INVOKED_BY, node);
456 } else if (element is ClassElement) {
457 recordRelation(element, IndexRelationKind.IS_REFERENCED_BY, node);
458 }
459 node.target?.accept(this);
460 node.argumentList?.accept(this);
461 }
462
463 @override
464 visitPartDirective(PartDirective node) {
465 recordRelation(node.element, IndexRelationKind.IS_REFERENCED_BY, node);
466 recordUriFileReference(node);
467 super.visitPartDirective(node);
468 }
469
470 @override
471 visitPartOfDirective(PartOfDirective node) {
472 recordRelation(node.element, IndexRelationKind.IS_REFERENCED_BY, node);
473 }
474
475 @override
476 visitPostfixExpression(PostfixExpression node) {
477 recordOperatorReference(node.operator, node.bestElement);
478 super.visitPostfixExpression(node);
479 }
480
481 @override
482 visitPrefixExpression(PrefixExpression node) {
483 recordOperatorReference(node.operator, node.bestElement);
484 super.visitPrefixExpression(node);
485 }
486
487 @override
488 visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
489 ConstructorElement element = node.staticElement;
490 if (node.constructorName != null) {
491 int offset = node.period.offset;
492 int length = node.constructorName.end - offset;
493 recordRelationOffset(
494 element, IndexRelationKind.IS_REFERENCED_BY, offset, length);
495 } else {
496 int offset = node.thisKeyword.end;
497 recordRelationOffset(
498 element, IndexRelationKind.IS_REFERENCED_BY, offset, 0);
499 }
500 super.visitRedirectingConstructorInvocation(node);
501 }
502
503 @override
504 visitSimpleIdentifier(SimpleIdentifier node) {
505 // TODO(scheglov) do we need this?
506 // IndexableName indexableName = new IndexableName(node.name);
507 // LocationImpl location = _createLocationForNode(node);
508 // if (location == null) {
509 // return;
510 // }
511 // name in declaration
512 if (node.inDeclarationContext()) {
513 // TODO(scheglov) do we need this?
514 // recordRelationshipIndexable(
515 // indexableName, IndexConstants.NAME_IS_DEFINED_BY, location);
516 return;
517 }
518 // name in an extends/with/implements clause
519 if (_isInExtendsWithImplementsClause(node)) {
520 return;
521 }
522 Element element = node.bestElement;
523 // this.field parameter
524 if (element is FieldFormalParameterElement) {
525 recordRelation(element.field, IndexRelationKind.IS_REFERENCED_BY, node);
526 return;
527 }
528 // record specific relations
529 // TODO(scheglov) consider removing the conditions
530 if (element is ClassElement ||
531 element is FunctionElement ||
532 element is FunctionTypeAliasElement ||
533 element is LabelElement ||
534 element is MethodElement ||
535 element is PrefixElement ||
536 element is PropertyAccessorElement ||
537 element is PropertyInducingElement ||
538 element is TypeParameterElement) {
539 recordRelation(element, IndexRelationKind.IS_REFERENCED_BY, node);
540 }
541 }
542
543 @override
544 visitSuperConstructorInvocation(SuperConstructorInvocation node) {
545 ConstructorElement element = node.staticElement;
546 if (node.constructorName != null) {
547 int offset = node.period.offset;
548 int length = node.constructorName.end - offset;
549 recordRelationOffset(
550 element, IndexRelationKind.IS_REFERENCED_BY, offset, length);
551 } else {
552 int offset = node.superKeyword.end;
553 recordRelationOffset(
554 element, IndexRelationKind.IS_REFERENCED_BY, offset, 0);
555 }
556 super.visitSuperConstructorInvocation(node);
557 }
558
559 @override
560 visitVariableDeclaration(VariableDeclaration node) {
561 VariableElement element = node.element;
562 recordTopLevelElementDefinition(element);
563 // TODO(scheglov) do we need this?
564 // // record declaration
565 // {
566 // SimpleIdentifier name = node.name;
567 // LocationImpl location = _createLocationForNode(name);
568 // location = _getLocationWithExpressionType(location, node.initializer);
569 // recordRelationshipElement(
570 // element, IndexConstants.NAME_IS_DEFINED_BY, location);
571 // }
572 super.visitVariableDeclaration(node);
573 }
574
575 static bool _isInExtendsWithImplementsClause(SimpleIdentifier node) {
576 TypeName typeName;
577 AstNode parent = node?.parent;
578 AstNode parent2 = parent?.parent;
579 if (parent is TypeName && parent.name == node) {
580 typeName = parent;
581 } else if (parent is PrefixedIdentifier &&
582 parent.identifier == node &&
583 parent2 is TypeName &&
584 parent2.name == node) {
585 typeName = parent2;
586 } else {
587 return false;
588 }
589 AstNode clause = typeName.parent;
590 return clause is ExtendsClause ||
591 clause is WithClause ||
592 clause is ImplementsClause;
593 }
594 }
595
596 /**
597 * Information about a single relation. Any [_RelationInfo] is always part
598 * of a [_UnitIndexAssembler], so [offset] and [length] should be understood
599 * within the context of the compilation unit pointed to by the
600 * [_UnitIndexAssembler].
601 */
602 class _RelationInfo {
603 final _ElementInfo elementInfo;
604 final IndexRelationKind kind;
605 final int offset;
606 final int length;
607
608 _RelationInfo(this.elementInfo, this.kind, this.offset, this.length);
609 }
610
611 /**
612 * Assembler of a single [CompilationUnit] index. The intended usage sequence:
613 *
614 * - Call [addRelation] for each relation found in the compilation unit.
615 * - Assign ids to all the [_ElementInfo] objects reachable from [relations].
616 * - Call [assemble] to produce the final unit index.
617 */
618 class _UnitIndexAssembler {
619 final PackageIndexAssembler pkg;
620 final CompilationUnitElement unitElement;
621 final List<_RelationInfo> relations = <_RelationInfo>[];
622
623 _UnitIndexAssembler(this.pkg, this.unitElement);
624
625 void addRelation(
626 Element element, IndexRelationKind kind, int offset, int length) {
627 try {
628 _ElementInfo elementInfo = pkg._getElementInfo(element);
629 relations.add(new _RelationInfo(elementInfo, kind, offset, length));
630 } on StateError {}
631 }
632
633 /**
634 * Assemble a new [UnitIndexBuilder] using the information gathered
635 * by [addRelation]
636 */
637 UnitIndexBuilder assemble() {
638 relations.sort((a, b) {
639 return a.elementInfo.id - b.elementInfo.id;
640 });
641 return new UnitIndexBuilder(
642 elements: relations.map((r) => r.elementInfo.id).toList(),
643 kinds: relations.map((r) => r.kind).toList(),
644 locationOffsets: relations.map((r) => r.offset).toList(),
645 locationLengths: relations.map((r) => r.length).toList(),
646 libraryUri: pkg._getUriId(unitElement.library.source.uri),
647 unitUri: pkg._getUriId(unitElement.source.uri));
648 }
649 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/test/src/abstract_single_unit.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698