OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library code_transformer.src.resolver_impl; | 5 library code_transformer.src.resolver_impl; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'package:analyzer/analyzer.dart' show parseDirectives; | 8 import 'package:analyzer/analyzer.dart' show parseDirectives; |
9 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
10 import 'package:analyzer/src/generated/constant.dart' | 10 import 'package:analyzer/src/generated/constant.dart' |
11 show ConstantEvaluator, EvaluationResult; | 11 show ConstantEvaluator, EvaluationResult; |
12 import 'package:analyzer/src/generated/element.dart'; | 12 import 'package:analyzer/dart/element/element.dart'; |
13 import 'package:analyzer/src/generated/engine.dart'; | 13 import 'package:analyzer/src/generated/engine.dart'; |
14 import 'package:analyzer/src/generated/sdk.dart' show DartSdk; | 14 import 'package:analyzer/src/generated/sdk.dart' show DartSdk; |
15 import 'package:analyzer/src/generated/source.dart'; | 15 import 'package:analyzer/src/generated/source.dart'; |
16 import 'package:barback/barback.dart'; | 16 import 'package:barback/barback.dart'; |
17 import 'package:code_transformers/assets.dart'; | 17 import 'package:code_transformers/assets.dart'; |
18 import 'package:path/path.dart' as native_path; | 18 import 'package:path/path.dart' as native_path; |
19 import 'package:source_maps/refactor.dart'; | 19 import 'package:source_maps/refactor.dart'; |
20 import 'package:source_span/source_span.dart'; | 20 import 'package:source_span/source_span.dart'; |
21 | 21 |
22 import 'resolver.dart'; | 22 import 'resolver.dart'; |
23 import 'dart_sdk.dart' show UriAnnotatedSource; | 23 import 'dart_sdk.dart' show UriAnnotatedSource; |
24 | 24 |
25 // We should always be using url paths here since it's always Dart/pub code. | 25 // We should always be using url paths here since it's always Dart/pub code. |
26 final path = native_path.url; | 26 final path = native_path.url; |
27 | 27 |
28 /// Resolves and updates an AST based on Barback-based assets. | 28 /// Resolves and updates an AST based on Barback-based assets. |
29 /// | 29 /// |
30 /// This also provides a handful of useful APIs for traversing and working | 30 /// This also provides a handful of useful APIs for traversing and working |
31 /// with the resolved AST. | 31 /// with the resolved AST. |
32 class ResolverImpl implements Resolver { | 32 class ResolverImpl implements Resolver { |
33 /// Cache of all asset sources currently referenced. | 33 /// Cache of all asset sources currently referenced. |
34 final Map<AssetId, _AssetBasedSource> sources = | 34 final Map<AssetId, AssetBasedSource> sources = {}; |
35 <AssetId, _AssetBasedSource>{}; | |
36 | 35 |
37 final InternalAnalysisContext _context = | 36 final InternalAnalysisContext _context = |
38 AnalysisEngine.instance.createAnalysisContext(); | 37 AnalysisEngine.instance.createAnalysisContext(); |
39 | 38 |
40 /// Transform for which this is currently updating, or null when not updating. | 39 /// Transform for which this is currently updating, or null when not updating. |
41 Transform _currentTransform; | 40 Transform _currentTransform; |
42 | 41 |
43 /// The currently resolved entry libraries, or null if nothing is resolved. | 42 /// The currently resolved entry libraries, or null if nothing is resolved. |
44 List<LibraryElement> _entryLibraries; | 43 List<LibraryElement> _entryLibraries; |
45 Set<LibraryElement> _libraries; | 44 Set<LibraryElement> _libraries; |
46 | 45 |
47 /// Future indicating when this resolver is done in the current phase. | 46 /// Future indicating when this resolver is done in the current phase. |
48 Future _lastPhaseComplete = new Future.value(); | 47 Future _lastPhaseComplete = new Future.value(); |
49 | 48 |
50 /// Completer for wrapping up the current phase. | 49 /// Completer for wrapping up the current phase. |
51 Completer _currentPhaseComplete; | 50 Completer _currentPhaseComplete; |
52 | 51 |
53 /// Creates a resolver with a given [sdk] implementation for resolving | 52 /// Creates a resolver with a given [sdk] implementation for resolving |
54 /// `dart:*` imports. | 53 /// `dart:*` imports. |
55 ResolverImpl(DartSdk sdk, DartUriResolver dartUriResolver, | 54 ResolverImpl(DartSdk sdk, DartUriResolver dartUriResolver, |
56 {AnalysisOptions options}) { | 55 {AnalysisOptions options}) { |
57 if (options == null) { | 56 if (options == null) { |
58 options = new AnalysisOptionsImpl() | 57 options = new AnalysisOptionsImpl() |
59 ..cacheSize = 256 // # of sources to cache ASTs for. | |
60 ..preserveComments = false | 58 ..preserveComments = false |
61 ..analyzeFunctionBodies = true; | 59 ..analyzeFunctionBodies = true; |
62 } | 60 } |
63 _context.analysisOptions = options; | 61 _context.analysisOptions = options; |
64 sdk.context.analysisOptions = options; | |
65 _context.sourceFactory = | 62 _context.sourceFactory = |
66 new SourceFactory([dartUriResolver, new _AssetUriResolver(this)]); | 63 new SourceFactory([dartUriResolver, new _AssetUriResolver(this)]); |
67 } | 64 } |
68 | 65 |
| 66 @override |
| 67 bool isLibrary(AssetId assetId) { |
| 68 var source = sources[assetId]; |
| 69 return source != null && _isLibrary(source); |
| 70 } |
| 71 |
| 72 bool _isLibrary(Source source) => |
| 73 _context.computeKindOf(source) == SourceKind.LIBRARY; |
| 74 |
| 75 @override |
69 LibraryElement getLibrary(AssetId assetId) { | 76 LibraryElement getLibrary(AssetId assetId) { |
70 var source = sources[assetId]; | 77 var source = sources[assetId]; |
71 return source == null ? null : _context.computeLibraryElement(source); | 78 if (source == null) return null; |
| 79 if (!_isLibrary(source)) return null; |
| 80 return _context.computeLibraryElement(source); |
72 } | 81 } |
73 | 82 |
74 Future<Resolver> resolve(Transform transform, [List<AssetId> entryPoints]) { | 83 Future<Resolver> resolve(Transform transform, |
| 84 [List<AssetId> entryPoints, bool resolveAllLibraries]) { |
75 // Can only have one resolve in progress at a time, so chain the current | 85 // Can only have one resolve in progress at a time, so chain the current |
76 // resolution to be after the last one. | 86 // resolution to be after the last one. |
77 var phaseComplete = new Completer(); | 87 var phaseComplete = new Completer(); |
78 var future = _lastPhaseComplete.whenComplete(() { | 88 var future = _lastPhaseComplete.whenComplete(() { |
79 _currentPhaseComplete = phaseComplete; | 89 _currentPhaseComplete = phaseComplete; |
80 return _performResolve(transform, | 90 return _performResolve( |
81 entryPoints == null ? [transform.primaryInput.id] : entryPoints); | 91 transform, |
| 92 entryPoints == null ? [transform.primaryInput.id] : entryPoints, |
| 93 resolveAllLibraries); |
82 }).then((_) => this); | 94 }).then((_) => this); |
83 // Advance the lastPhaseComplete to be done when this phase is all done. | 95 // Advance the lastPhaseComplete to be done when this phase is all done. |
84 _lastPhaseComplete = phaseComplete.future; | 96 _lastPhaseComplete = phaseComplete.future; |
85 return future; | 97 return future; |
86 } | 98 } |
87 | 99 |
88 void release() { | 100 void release() { |
89 if (_currentPhaseComplete == null) { | 101 if (_currentPhaseComplete == null) { |
90 throw new StateError('Releasing without current lock.'); | 102 throw new StateError('Releasing without current lock.'); |
91 } | 103 } |
92 _currentPhaseComplete.complete(null); | 104 _currentPhaseComplete.complete(null); |
93 _currentPhaseComplete = null; | 105 _currentPhaseComplete = null; |
94 | 106 |
95 // Clear out libraries since they should not be referenced after release. | 107 // Clear out libraries since they should not be referenced after release. |
96 _entryLibraries = null; | 108 _entryLibraries = null; |
97 _libraries = null; | 109 _libraries = null; |
98 _currentTransform = null; | 110 _currentTransform = null; |
99 } | 111 } |
100 | 112 |
101 Future _performResolve(Transform transform, List<AssetId> entryPoints) { | 113 Future _performResolve(Transform transform, List<AssetId> entryPoints, |
| 114 bool resolveAllLibraries) { |
| 115 resolveAllLibraries ??= true; |
102 if (_currentTransform != null) { | 116 if (_currentTransform != null) { |
103 throw new StateError('Cannot be accessed by concurrent transforms'); | 117 throw new StateError('Cannot be accessed by concurrent transforms'); |
104 } | 118 } |
105 _currentTransform = transform; | 119 _currentTransform = transform; |
106 | 120 |
107 // Basic approach is to start at the first file, update it's contents | 121 // Basic approach is to start at the first file, update it's contents |
108 // and see if it changed, then walk all files accessed by it. | 122 // and see if it changed, then walk all files accessed by it. |
109 var visited = new Set<AssetId>(); | 123 var visited = new Set<AssetId>(); |
110 var visiting = new FutureGroup(); | 124 var visiting = new FutureGroup(); |
111 var toUpdate = []; | 125 var toUpdate = []; |
112 | 126 |
113 void processAsset(AssetId assetId) { | 127 void processAsset(AssetId assetId) { |
114 visited.add(assetId); | 128 visited.add(assetId); |
115 | 129 |
116 visiting.add(transform.readInputAsString(assetId).then((contents) { | 130 visiting.add(transform.readInputAsString(assetId).then((contents) { |
117 var source = sources[assetId]; | 131 var source = sources[assetId]; |
118 if (source == null) { | 132 if (source == null) { |
119 source = new _AssetBasedSource(assetId, this); | 133 source = new AssetBasedSource(assetId, this); |
120 sources[assetId] = source; | 134 sources[assetId] = source; |
121 } | 135 } |
122 source.updateDependencies(contents); | 136 source.updateDependencies(contents); |
123 toUpdate.add(new _PendingUpdate(source, contents)); | 137 toUpdate.add(new _PendingUpdate(source, contents)); |
124 source.dependentAssets | 138 source.dependentAssets |
125 .where((id) => !visited.contains(id)) | 139 .where((id) => !visited.contains(id)) |
126 .forEach(processAsset); | 140 .forEach(processAsset); |
127 }, onError: (e) { | 141 }, onError: (e) { |
128 var source = sources[assetId]; | 142 var source = sources[assetId]; |
129 if (source != null && source.exists()) { | 143 if (source != null && source.exists()) { |
130 _context.applyChanges(new ChangeSet()..removedSource(source)); | 144 _context.applyChanges(new ChangeSet()..removedSource(source)); |
131 sources[assetId].updateContents(null); | 145 sources[assetId].updateContents(null); |
132 } | 146 } |
133 })); | 147 })); |
134 } | 148 } |
| 149 |
135 entryPoints.forEach(processAsset); | 150 entryPoints.forEach(processAsset); |
136 | 151 |
137 // Once we have all asset sources updated with the new contents then | 152 // Once we have all asset sources updated with the new contents then |
138 // resolve everything. | 153 // resolve everything. |
139 return visiting.future.then((_) { | 154 return visiting.future.then((_) { |
140 var changeSet = new ChangeSet(); | 155 var changeSet = new ChangeSet(); |
141 toUpdate.forEach((pending) => pending.apply(changeSet)); | 156 toUpdate.forEach((pending) => pending.apply(changeSet)); |
142 var unreachableAssets = | |
143 sources.keys.toSet().difference(visited).map((id) => sources[id]); | |
144 for (var unreachable in unreachableAssets) { | |
145 changeSet.removedSource(unreachable); | |
146 unreachable.updateContents(null); | |
147 sources.remove(unreachable.assetId); | |
148 } | |
149 | 157 |
150 // Update the analyzer context with the latest sources | 158 // Update the analyzer context with the latest sources |
151 _context.applyChanges(changeSet); | 159 _context.applyChanges(changeSet); |
152 // Force resolve each entry point (the getter will ensure the library is | 160 // Force resolve each entry point (the getter will ensure the library is |
153 // computed first). | 161 // computed first). |
154 _entryLibraries = entryPoints.map((id) { | 162 _entryLibraries = entryPoints.map((id) { |
155 var source = sources[id]; | 163 var source = sources[id]; |
156 if (source == null) return null; | 164 if (source == null) return null; |
| 165 var kind = _context.computeKindOf(source); |
| 166 if (kind != SourceKind.LIBRARY) return null; |
157 return _context.computeLibraryElement(source); | 167 return _context.computeLibraryElement(source); |
158 }).toList(); | 168 }).toList(); |
| 169 |
| 170 if (resolveAllLibraries) { |
| 171 // Force resolve all other available libraries. As of analyzer > 0.27.1 |
| 172 // this is necessary to get resolved constants. |
| 173 var newLibraries = new Set<LibraryElement>(); |
| 174 for (var library in libraries) { |
| 175 if (library.source.uri.scheme == 'dart' || |
| 176 _entryLibraries.contains(library)) { |
| 177 newLibraries.add(library); |
| 178 } else { |
| 179 newLibraries.add(_context |
| 180 .computeLibraryElement(library.definingCompilationUnit.source)); |
| 181 } |
| 182 } |
| 183 _libraries = newLibraries; |
| 184 } |
159 }); | 185 }); |
160 } | 186 } |
161 | 187 |
162 Iterable<LibraryElement> get libraries { | 188 Iterable<LibraryElement> get libraries { |
163 if (_libraries == null) { | 189 if (_libraries == null) { |
164 // Note: we don't use `lib.visibleLibraries` because that excludes the | 190 // Note: we don't use `lib.visibleLibraries` because that excludes the |
165 // exports seen in the entry libraries. | 191 // exports seen in the entry libraries. |
166 _libraries = new Set<LibraryElement>(); | 192 _libraries = new Set<LibraryElement>(); |
167 _entryLibraries.forEach(_collectLibraries); | 193 _entryLibraries.forEach(_collectLibraries); |
168 } | 194 } |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
229 .evaluate(expression); | 255 .evaluate(expression); |
230 } | 256 } |
231 | 257 |
232 Uri getImportUri(LibraryElement lib, {AssetId from}) => | 258 Uri getImportUri(LibraryElement lib, {AssetId from}) => |
233 _getSourceUri(lib, from: from); | 259 _getSourceUri(lib, from: from); |
234 | 260 |
235 /// Similar to getImportUri but will get the part URI for parts rather than | 261 /// Similar to getImportUri but will get the part URI for parts rather than |
236 /// the library URI. | 262 /// the library URI. |
237 Uri _getSourceUri(Element element, {AssetId from}) { | 263 Uri _getSourceUri(Element element, {AssetId from}) { |
238 var source = element.source; | 264 var source = element.source; |
239 if (source is _AssetBasedSource) { | 265 if (source is AssetBasedSource) { |
240 var uriString = assetIdToUri(source.assetId, from: from); | 266 var uriString = assetIdToUri(source.assetId, from: from); |
241 return uriString != null ? Uri.parse(uriString) : null; | 267 return uriString != null ? Uri.parse(uriString) : null; |
242 } else if (source is UriAnnotatedSource) { | 268 } else if (source is UriAnnotatedSource) { |
243 return source.uri; | 269 return source.uri; |
244 } | 270 } |
245 // Should not be able to encounter any other source types. | 271 // Should not be able to encounter any other source types. |
246 throw new StateError('Unable to resolve URI for ${source.runtimeType}'); | 272 throw new StateError('Unable to resolve URI for ${source.runtimeType}'); |
247 } | 273 } |
248 | 274 |
249 AssetId getSourceAssetId(Element element) { | 275 AssetId getSourceAssetId(Element element) { |
250 var source = element.source; | 276 var source = element.source; |
251 if (source is _AssetBasedSource) return source.assetId; | 277 if (source is AssetBasedSource) return source.assetId; |
252 return null; | 278 return null; |
253 } | 279 } |
254 | 280 |
255 SourceSpan getSourceSpan(Element element) { | 281 SourceSpan getSourceSpan(Element element) { |
256 var sourceFile = getSourceFile(element); | 282 var sourceFile = getSourceFile(element); |
257 if (sourceFile == null) return null; | 283 if (sourceFile == null) return null; |
258 return sourceFile.span(element.node.offset, element.node.end); | 284 return sourceFile.span( |
| 285 element.computeNode().offset, element.computeNode().end); |
259 } | 286 } |
260 | 287 |
261 TextEditTransaction createTextEditTransaction(Element element) { | 288 TextEditTransaction createTextEditTransaction(Element element) { |
262 if (element.source is! _AssetBasedSource) return null; | 289 if (element.source is! AssetBasedSource) return null; |
263 | 290 |
264 // Cannot edit unless there is an active transformer. | 291 // Cannot edit unless there is an active transformer. |
265 if (_currentTransform == null) return null; | 292 if (_currentTransform == null) return null; |
266 | 293 |
267 _AssetBasedSource source = element.source; | 294 AssetBasedSource source = element.source; |
268 // Cannot modify assets in other packages. | 295 // Cannot modify assets in other packages. |
269 if (source.assetId.package != _currentTransform.primaryInput.id.package) { | 296 if (source.assetId.package != _currentTransform.primaryInput.id.package) { |
270 return null; | 297 return null; |
271 } | 298 } |
272 | 299 |
273 var sourceFile = getSourceFile(element); | 300 var sourceFile = getSourceFile(element); |
274 if (sourceFile == null) return null; | 301 if (sourceFile == null) return null; |
275 | 302 |
276 return new TextEditTransaction(source.rawContents, sourceFile); | 303 return new TextEditTransaction(source.rawContents, sourceFile); |
277 } | 304 } |
278 | 305 |
279 /// Gets the SourceFile for the source of the element. | 306 /// Gets the SourceFile for the source of the element. |
280 SourceFile getSourceFile(Element element) { | 307 SourceFile getSourceFile(Element element) { |
281 var assetId = getSourceAssetId(element); | 308 var assetId = getSourceAssetId(element); |
282 if (assetId == null) return null; | 309 if (assetId == null) return null; |
283 | 310 |
284 var importUri = _getSourceUri(element); | 311 var importUri = _getSourceUri(element); |
285 var spanPath = importUri != null ? importUri.toString() : assetId.path; | 312 var spanPath = importUri != null ? importUri.toString() : assetId.path; |
286 return new SourceFile(sources[assetId].rawContents, url: spanPath); | 313 return new SourceFile(sources[assetId].rawContents, url: spanPath); |
287 } | 314 } |
288 } | 315 } |
289 | 316 |
290 /// Implementation of Analyzer's Source for Barback based assets. | 317 /// Implementation of Analyzer's Source for Barback based assets. |
291 class _AssetBasedSource extends Source { | 318 class AssetBasedSource extends Source { |
292 | |
293 /// Asset ID where this source can be found. | 319 /// Asset ID where this source can be found. |
294 final AssetId assetId; | 320 final AssetId assetId; |
295 | 321 |
296 /// The resolver this is being used in. | 322 /// The resolver this is being used in. |
297 final ResolverImpl _resolver; | 323 final ResolverImpl _resolver; |
298 | 324 |
299 /// Cache of dependent asset IDs, to avoid re-parsing the AST. | 325 /// Cache of dependent asset IDs, to avoid re-parsing the AST. |
300 Iterable<AssetId> _dependentAssets; | 326 Iterable<AssetId> _dependentAssets; |
301 | 327 |
302 /// The current revision of the file, incremented only when file changes. | 328 /// The current revision of the file, incremented only when file changes. |
303 int _revision = 0; | 329 int _revision = 0; |
304 | 330 |
305 /// The file contents. | 331 /// The file contents. |
306 String _contents; | 332 String _contents; |
307 | 333 |
308 _AssetBasedSource(this.assetId, this._resolver); | 334 AssetBasedSource(this.assetId, this._resolver); |
309 | 335 |
310 /// Update the dependencies of this source. This parses [contents] but avoids | 336 /// Update the dependencies of this source. This parses [contents] but avoids |
311 /// any analyzer resolution. | 337 /// any analyzer resolution. |
312 void updateDependencies(String contents) { | 338 void updateDependencies(String contents) { |
313 if (contents == _contents) return; | 339 if (contents == _contents) return; |
314 var unit = parseDirectives(contents, suppressErrors: true); | 340 var unit = parseDirectives(contents, suppressErrors: true); |
315 _dependentAssets = unit.directives | 341 _dependentAssets = unit.directives |
316 .where((d) => (d is ImportDirective || | 342 .where((d) => d is UriBasedDirective) |
317 d is PartDirective || | 343 .map((d) => _resolve(assetId, (d as UriBasedDirective).uri.stringValue, |
318 d is ExportDirective)) | 344 _logger, _getSpan(d, contents))) |
319 .map((d) => _resolve( | |
320 assetId, d.uri.stringValue, _logger, _getSpan(d, contents))) | |
321 .where((id) => id != null) | 345 .where((id) => id != null) |
322 .toSet(); | 346 .toSet(); |
323 } | 347 } |
324 | 348 |
325 /// Update the contents of this file with [contents]. | 349 /// Update the contents of this file with [contents]. |
326 /// | 350 /// |
327 /// Returns true if the contents of this asset have changed. | 351 /// Returns true if the contents of this asset have changed. |
328 bool updateContents(String contents) { | 352 bool updateContents(String contents) { |
329 if (contents == _contents) return false; | 353 if (contents == _contents) return false; |
330 _contents = contents; | 354 _contents = contents; |
(...skipping 17 matching lines...) Expand all Loading... |
348 /// | 372 /// |
349 /// Only valid while the resolver is updating assets. | 373 /// Only valid while the resolver is updating assets. |
350 TransformLogger get _logger => _resolver._currentTransform.logger; | 374 TransformLogger get _logger => _resolver._currentTransform.logger; |
351 | 375 |
352 /// Gets all imports/parts/exports which resolve to assets (non-Dart files). | 376 /// Gets all imports/parts/exports which resolve to assets (non-Dart files). |
353 Iterable<AssetId> get dependentAssets => _dependentAssets; | 377 Iterable<AssetId> get dependentAssets => _dependentAssets; |
354 | 378 |
355 bool exists() => _contents != null; | 379 bool exists() => _contents != null; |
356 | 380 |
357 bool operator ==(Object other) => | 381 bool operator ==(Object other) => |
358 other is _AssetBasedSource && assetId == other.assetId; | 382 other is AssetBasedSource && assetId == other.assetId; |
359 | 383 |
360 int get hashCode => assetId.hashCode; | 384 int get hashCode => assetId.hashCode; |
361 | 385 |
362 void getContentsToReceiver(Source_ContentReceiver receiver) { | 386 void getContentsToReceiver(Source_ContentReceiver receiver) { |
363 receiver.accept(rawContents, modificationStamp); | 387 receiver.accept(rawContents, modificationStamp); |
364 } | 388 } |
365 | 389 |
366 String get encoding => | 390 String get encoding => |
367 "${uriKind.encoding}${assetId.package}/${assetId.path}"; | 391 "${uriKind.encoding}${assetId.package}/${assetId.path}"; |
368 | 392 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
430 assetId = _resolve(null, uri.toString(), logger, null); | 454 assetId = _resolve(null, uri.toString(), logger, null); |
431 if (assetId == null) { | 455 if (assetId == null) { |
432 logger.error('Unable to resolve asset ID for "$uri"'); | 456 logger.error('Unable to resolve asset ID for "$uri"'); |
433 return null; | 457 return null; |
434 } | 458 } |
435 } | 459 } |
436 var source = _resolver.sources[assetId]; | 460 var source = _resolver.sources[assetId]; |
437 // Analyzer expects that sources which are referenced but do not exist yet | 461 // Analyzer expects that sources which are referenced but do not exist yet |
438 // still exist, so just make an empty source. | 462 // still exist, so just make an empty source. |
439 if (source == null) { | 463 if (source == null) { |
440 source = new _AssetBasedSource(assetId, _resolver); | 464 source = new AssetBasedSource(assetId, _resolver); |
441 _resolver.sources[assetId] = source; | 465 _resolver.sources[assetId] = source; |
442 } | 466 } |
443 return source; | 467 return source; |
444 } | 468 } |
445 | 469 |
446 Source fromEncoding(UriKind kind, Uri uri) => | 470 Source fromEncoding(UriKind kind, Uri uri) => |
447 throw new UnsupportedError('fromEncoding is not supported'); | 471 throw new UnsupportedError('fromEncoding is not supported'); |
448 | 472 |
449 Uri restoreAbsolute(Source source) => | 473 Uri restoreAbsolute(Source source) => |
450 throw new UnsupportedError('restoreAbsolute is not supported'); | 474 throw new UnsupportedError('restoreAbsolute is not supported'); |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
531 * error will be sent to the Future. | 555 * error will be sent to the Future. |
532 */ | 556 */ |
533 Future<List<E>> get future => _completer.future; | 557 Future<List<E>> get future => _completer.future; |
534 } | 558 } |
535 | 559 |
536 /// A pending update to notify the resolver that a [Source] has been added or | 560 /// A pending update to notify the resolver that a [Source] has been added or |
537 /// changed. This is used by the `_performResolve` algorithm above to apply all | 561 /// changed. This is used by the `_performResolve` algorithm above to apply all |
538 /// changes after it first discovers the transitive closure of files that are | 562 /// changes after it first discovers the transitive closure of files that are |
539 /// reachable from the sources. | 563 /// reachable from the sources. |
540 class _PendingUpdate { | 564 class _PendingUpdate { |
541 _AssetBasedSource source; | 565 AssetBasedSource source; |
542 String content; | 566 String content; |
543 | 567 |
544 _PendingUpdate(this.source, this.content); | 568 _PendingUpdate(this.source, this.content); |
545 | 569 |
546 void apply(ChangeSet changeSet) { | 570 void apply(ChangeSet changeSet) { |
547 if (!source.updateContents(content)) return; | 571 if (!source.updateContents(content)) return; |
548 if (source._revision == 1 && source._contents != null) { | 572 if (source._revision == 1 && source._contents != null) { |
549 changeSet.addedSource(source); | 573 changeSet.addedSource(source); |
550 } else { | 574 } else { |
551 changeSet.changedSource(source); | 575 changeSet.changedSource(source); |
552 } | 576 } |
553 } | 577 } |
554 } | 578 } |
OLD | NEW |