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

Side by Side Diff: analyzer/lib/src/task/incremental_element_builder.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 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 | « analyzer/lib/src/task/html_work_manager.dart ('k') | analyzer/lib/src/task/inputs.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) 2015, 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 library analyzer.src.task.incremental_element_builder;
6
7 import 'dart:collection';
8
9 import 'package:analyzer/src/generated/ast.dart';
10 import 'package:analyzer/src/generated/element.dart';
11 import 'package:analyzer/src/generated/resolver.dart';
12 import 'package:analyzer/src/generated/scanner.dart';
13 import 'package:analyzer/src/generated/source.dart';
14
15 /**
16 * The change of a single [CompilationUnitElement].
17 */
18 class CompilationUnitElementDelta {
19 /**
20 * One or more directives were added/removed.
21 */
22 bool hasDirectiveChange = false;
23
24 /**
25 * The list of added top-level element.
26 */
27 final List<Element> addedDeclarations = <Element>[];
28
29 /**
30 * The list of removed top-level elements.
31 */
32 final List<Element> removedDeclarations = <Element>[];
33 }
34
35 /**
36 * Incrementally updates the existing [unitElement] and builds elements for
37 * the [newUnit].
38 */
39 class IncrementalCompilationUnitElementBuilder {
40 final Source unitSource;
41 final Source librarySource;
42 final CompilationUnit oldUnit;
43 final CompilationUnitElementImpl unitElement;
44 final CompilationUnit newUnit;
45 final ElementHolder holder = new ElementHolder();
46
47 /**
48 * The change between element models of [oldUnit] and [newUnit].
49 */
50 final CompilationUnitElementDelta unitDelta =
51 new CompilationUnitElementDelta();
52
53 factory IncrementalCompilationUnitElementBuilder(
54 CompilationUnit oldUnit, CompilationUnit newUnit) {
55 CompilationUnitElementImpl unitElement = oldUnit.element;
56 return new IncrementalCompilationUnitElementBuilder._(unitElement.source,
57 unitElement.librarySource, oldUnit, newUnit, unitElement);
58 }
59
60 IncrementalCompilationUnitElementBuilder._(this.unitSource,
61 this.librarySource, this.oldUnit, this.newUnit, this.unitElement);
62
63 /**
64 * Updates [oldUnit] to have the same directives and declarations, in the
65 * same order as in [newUnit]. Existing resolution is kept where possible.
66 *
67 * Updates [unitElement] by adding/removing elements as needed.
68 *
69 * Fills [unitDelta] with added/remove elements.
70 */
71 void build() {
72 new CompilationUnitBuilder().buildCompilationUnit(
73 unitSource, newUnit, librarySource);
74 _processDirectives();
75 _processUnitMembers();
76 newUnit.element = unitElement;
77 _replaceUnitContents(oldUnit, newUnit);
78 }
79
80 void _addElementToHolder(Element element) {
81 if (element is PropertyAccessorElement) {
82 holder.addAccessor(element);
83 } else if (element is ClassElement) {
84 if (element.isEnum) {
85 holder.addEnum(element);
86 } else {
87 holder.addType(element);
88 }
89 } else if (element is FunctionElement) {
90 holder.addFunction(element);
91 } else if (element is FunctionTypeAliasElement) {
92 holder.addTypeAlias(element);
93 } else if (element is TopLevelVariableElement) {
94 holder.addTopLevelVariable(element);
95 }
96 }
97
98 void _processDirectives() {
99 Map<String, Directive> oldDirectiveMap = new HashMap<String, Directive>();
100 for (Directive oldDirective in oldUnit.directives) {
101 String code = TokenUtils.getFullCode(oldDirective);
102 oldDirectiveMap[code] = oldDirective;
103 }
104 // Replace new nodes with the identical old nodes.
105 Set<Directive> removedDirectives = oldUnit.directives.toSet();
106 for (Directive newDirective in newUnit.directives) {
107 String code = TokenUtils.getFullCode(newDirective);
108 // Prepare an old directive.
109 Directive oldDirective = oldDirectiveMap[code];
110 if (oldDirective == null) {
111 unitDelta.hasDirectiveChange = true;
112 continue;
113 }
114 // URI's must be resolved to the same sources.
115 if (newDirective is UriBasedDirective &&
116 oldDirective is UriBasedDirective) {
117 if (oldDirective.source != newDirective.source) {
118 continue;
119 }
120 }
121 // Do replacement.
122 _replaceNode(newDirective, oldDirective);
123 removedDirectives.remove(oldDirective);
124 }
125 // If there are any directives left, then these directives were removed.
126 if (removedDirectives.isNotEmpty) {
127 unitDelta.hasDirectiveChange = true;
128 }
129 }
130
131 void _processUnitMembers() {
132 Map<String, CompilationUnitMember> oldNodeMap =
133 new HashMap<String, CompilationUnitMember>();
134 for (CompilationUnitMember oldNode in oldUnit.declarations) {
135 String code = TokenUtils.getFullCode(oldNode);
136 oldNodeMap[code] = oldNode;
137 }
138 // Prepare all old top-level elements.
139 Set<Element> removedElements = new Set<Element>();
140 removedElements.addAll(unitElement.accessors);
141 removedElements.addAll(unitElement.enums);
142 removedElements.addAll(unitElement.functions);
143 removedElements.addAll(unitElement.functionTypeAliases);
144 removedElements.addAll(unitElement.types);
145 removedElements.addAll(unitElement.topLevelVariables);
146 // Replace new nodes with the identical old nodes.
147 for (CompilationUnitMember newNode in newUnit.declarations) {
148 String code = TokenUtils.getFullCode(newNode);
149 // Prepare an old node.
150 CompilationUnitMember oldNode = oldNodeMap[code];
151 if (oldNode == null) {
152 List<Element> elements = _getElements(newNode);
153 elements.forEach(_addElementToHolder);
154 elements.forEach(unitDelta.addedDeclarations.add);
155 continue;
156 }
157 // Do replacement.
158 _replaceNode(newNode, oldNode);
159 List<Element> elements = _getElements(oldNode);
160 elements.forEach(_addElementToHolder);
161 elements.forEach(removedElements.remove);
162 }
163 unitDelta.removedDeclarations.addAll(removedElements);
164 // Update CompilationUnitElement.
165 unitElement.accessors = holder.accessors;
166 unitElement.enums = holder.enums;
167 unitElement.functions = holder.functions;
168 unitElement.typeAliases = holder.typeAliases;
169 unitElement.types = holder.types;
170 unitElement.topLevelVariables = holder.topLevelVariables;
171 holder.validate();
172 }
173
174 /**
175 * Replaces [newNode] with [oldNode], updates tokens and elements.
176 * The nodes must have the same tokens, but offsets may be different.
177 */
178 void _replaceNode(AstNode newNode, AstNode oldNode) {
179 // Replace node.
180 NodeReplacer.replace(newNode, oldNode);
181 // Replace tokens.
182 {
183 Token oldBeginToken = TokenUtils.getBeginTokenNotComment(newNode);
184 Token newBeginToken = TokenUtils.getBeginTokenNotComment(oldNode);
185 oldBeginToken.previous.setNext(newBeginToken);
186 oldNode.endToken.setNext(newNode.endToken.next);
187 }
188 // Change tokens offsets.
189 Map<int, int> offsetMap = new HashMap<int, int>();
190 TokenUtils.copyTokenOffsets(offsetMap, oldNode.beginToken,
191 newNode.beginToken, oldNode.endToken, newNode.endToken, true);
192 // Change elements offsets.
193 {
194 var visitor = new _UpdateElementOffsetsVisitor(offsetMap);
195 List<Element> elements = _getElements(oldNode);
196 for (Element element in elements) {
197 element.accept(visitor);
198 }
199 }
200 }
201
202 /**
203 * Returns [Element]s that are declared directly by the given [node].
204 * This does not include any child elements - parameters, local variables.
205 *
206 * Usually just one [Element] is returned, but [VariableDeclarationList]
207 * nodes may declare more than one.
208 */
209 static List<Element> _getElements(AstNode node) {
210 List<Element> elements = <Element>[];
211 if (node is TopLevelVariableDeclaration) {
212 VariableDeclarationList variableList = node.variables;
213 if (variableList != null) {
214 for (VariableDeclaration variable in variableList.variables) {
215 TopLevelVariableElement element = variable.element;
216 elements.add(element);
217 if (element.getter != null) {
218 elements.add(element.getter);
219 }
220 if (element.setter != null) {
221 elements.add(element.setter);
222 }
223 }
224 }
225 } else if (node is PartDirective || node is PartOfDirective) {
226 } else if (node is Directive && node.element != null) {
227 elements.add(node.element);
228 } else if (node is Declaration && node.element != null) {
229 Element element = node.element;
230 elements.add(element);
231 if (element is PropertyAccessorElement) {
232 elements.add(element.variable);
233 }
234 }
235 return elements;
236 }
237
238 /**
239 * Replaces contents of the [to] unit with the contenxts of the [from] unit.
240 */
241 static void _replaceUnitContents(CompilationUnit to, CompilationUnit from) {
242 to.directives.clear();
243 to.declarations.clear();
244 to.beginToken = from.beginToken;
245 to.scriptTag = from.scriptTag;
246 to.directives.addAll(from.directives);
247 to.declarations.addAll(from.declarations);
248 to.element = to.element;
249 to.lineInfo = from.lineInfo;
250 }
251 }
252
253 /**
254 * Utilities for [Token] manipulations.
255 */
256 class TokenUtils {
257 static const String _SEPARATOR = "\uFFFF";
258
259 /**
260 * Copy offsets from [newToken]s to [oldToken]s.
261 */
262 static void copyTokenOffsets(Map<int, int> offsetMap, Token oldToken,
263 Token newToken, Token oldEndToken, Token newEndToken,
264 [bool goUpComment = false]) {
265 if (oldToken is CommentToken && newToken is CommentToken) {
266 if (goUpComment) {
267 copyTokenOffsets(offsetMap, (oldToken as CommentToken).parent,
268 (newToken as CommentToken).parent, oldEndToken, newEndToken);
269 }
270 while (oldToken != null) {
271 offsetMap[oldToken.offset] = newToken.offset;
272 oldToken.offset = newToken.offset;
273 oldToken = oldToken.next;
274 newToken = newToken.next;
275 }
276 assert(oldToken == null);
277 assert(newToken == null);
278 return;
279 }
280 while (true) {
281 if (oldToken.precedingComments != null) {
282 assert(newToken.precedingComments != null);
283 copyTokenOffsets(offsetMap, oldToken.precedingComments,
284 newToken.precedingComments, oldEndToken, newEndToken);
285 }
286 offsetMap[oldToken.offset] = newToken.offset;
287 oldToken.offset = newToken.offset;
288 if (oldToken == oldEndToken) {
289 assert(newToken == newEndToken);
290 break;
291 }
292 oldToken = oldToken.next;
293 newToken = newToken.next;
294 }
295 }
296
297 static Token getBeginTokenNotComment(AstNode node) {
298 Token oldBeginToken = node.beginToken;
299 if (oldBeginToken is CommentToken) {
300 oldBeginToken = (oldBeginToken as CommentToken).parent;
301 }
302 return oldBeginToken;
303 }
304
305 /**
306 * Return the token string of all the [node] tokens.
307 */
308 static String getFullCode(AstNode node) {
309 List<Token> tokens = getTokens(node);
310 return joinTokens(tokens);
311 }
312
313 /**
314 * Returns all tokends (including comments) of the given [node].
315 */
316 static List<Token> getTokens(AstNode node) {
317 List<Token> tokens = <Token>[];
318 Token token = getBeginTokenNotComment(node);
319 Token endToken = node.endToken;
320 while (true) {
321 // append comment tokens
322 for (Token commentToken = token.precedingComments;
323 commentToken != null;
324 commentToken = commentToken.next) {
325 tokens.add(commentToken);
326 }
327 // append token
328 tokens.add(token);
329 // next token
330 if (token == endToken) {
331 break;
332 }
333 token = token.next;
334 }
335 return tokens;
336 }
337
338 static String joinTokens(List<Token> tokens) {
339 return tokens.map((token) => token.lexeme).join(_SEPARATOR);
340 }
341 }
342
343 /**
344 * Updates name offsets of [Element]s according to the [map].
345 */
346 class _UpdateElementOffsetsVisitor extends GeneralizingElementVisitor {
347 final Map<int, int> map;
348
349 _UpdateElementOffsetsVisitor(this.map);
350
351 void visitElement(Element element) {
352 if (element is CompilationUnitElement) {
353 return;
354 }
355 if (element.isSynthetic) {
356 return;
357 }
358 int oldOffset = element.nameOffset;
359 int newOffset = map[oldOffset];
360 assert(newOffset != null);
361 (element as ElementImpl).nameOffset = newOffset;
362 if (element is! LibraryElement) {
363 super.visitElement(element);
364 }
365 }
366 }
OLDNEW
« no previous file with comments | « analyzer/lib/src/task/html_work_manager.dart ('k') | analyzer/lib/src/task/inputs.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698