| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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 /// Tracks the shape of the import/export graph and dependencies between files. | 5 /// Tracks the shape of the import/export graph and dependencies between files. |
| 6 library dev_compiler.src.dependency_graph; | 6 library dev_compiler.src.dependency_graph; |
| 7 | 7 |
| 8 import 'dart:collection' show HashSet; | 8 import 'dart:collection' show HashSet; |
| 9 | 9 |
| 10 import 'package:analyzer/analyzer.dart' show parseDirectives; | 10 import 'package:analyzer/analyzer.dart' show parseDirectives; |
| 11 import 'package:analyzer/src/generated/ast.dart' | 11 import 'package:analyzer/src/generated/ast.dart' |
| 12 show | 12 show |
| 13 AstNode, | 13 AstNode, |
| 14 CompilationUnit, | 14 CompilationUnit, |
| 15 ExportDirective, | 15 ExportDirective, |
| 16 Identifier, | 16 Identifier, |
| 17 ImportDirective, | 17 ImportDirective, |
| 18 LibraryDirective, | 18 LibraryDirective, |
| 19 PartDirective, | 19 PartDirective, |
| 20 PartOfDirective; | 20 PartOfDirective; |
| 21 import 'package:analyzer/src/generated/engine.dart' | 21 import 'package:analyzer/src/generated/engine.dart' |
| 22 show ParseDartTask, AnalysisContext; | 22 show ParseDartTask, AnalysisContext; |
| 23 import 'package:analyzer/src/generated/source.dart' show Source, SourceKind; | 23 import 'package:analyzer/src/generated/source.dart' show Source, SourceKind; |
| 24 import 'package:html/dom.dart' show Document, Node; | 24 import 'package:html/dom.dart' show Document, Node; |
| 25 import 'package:html/parser.dart' as html; | 25 import 'package:html/parser.dart' as html; |
| 26 import 'package:logging/logging.dart' show Logger, Level; | 26 import 'package:logging/logging.dart' show Logger, Level; |
| 27 import 'package:path/path.dart' as path; | 27 import 'package:path/path.dart' as path; |
| 28 import 'package:source_span/source_span.dart' show SourceSpan; | |
| 29 | 28 |
| 30 import 'info.dart'; | 29 import 'info.dart'; |
| 31 import 'options.dart'; | 30 import 'options.dart'; |
| 32 import 'report.dart'; | 31 import 'report.dart'; |
| 33 import 'utils.dart'; | |
| 34 | 32 |
| 35 /// Holds references to all source nodes in the import graph. This is mainly | 33 /// Holds references to all source nodes in the import graph. This is mainly |
| 36 /// used as a level of indirection to ensure that each source has a canonical | 34 /// used as a level of indirection to ensure that each source has a canonical |
| 37 /// representation. | 35 /// representation. |
| 38 class SourceGraph { | 36 class SourceGraph { |
| 39 /// All nodes in the source graph. Used to get a canonical representation for | 37 /// All nodes in the source graph. Used to get a canonical representation for |
| 40 /// any node. | 38 /// any node. |
| 41 final Map<Uri, SourceNode> nodes = {}; | 39 final Map<Uri, SourceNode> nodes = {}; |
| 42 | 40 |
| 43 /// Resources included by default on any application. | 41 /// Resources included by default on any application. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 63 } | 61 } |
| 64 } | 62 } |
| 65 | 63 |
| 66 /// Node associated with a resolved [uri]. | 64 /// Node associated with a resolved [uri]. |
| 67 SourceNode nodeFromUri(Uri uri) { | 65 SourceNode nodeFromUri(Uri uri) { |
| 68 var uriString = Uri.encodeFull('$uri'); | 66 var uriString = Uri.encodeFull('$uri'); |
| 69 return nodes.putIfAbsent(uri, () { | 67 return nodes.putIfAbsent(uri, () { |
| 70 var source = _context.sourceFactory.forUri(uriString); | 68 var source = _context.sourceFactory.forUri(uriString); |
| 71 var extension = path.extension(uriString); | 69 var extension = path.extension(uriString); |
| 72 if (extension == '.html') { | 70 if (extension == '.html') { |
| 73 return new HtmlSourceNode(uri, source, this); | 71 return new HtmlSourceNode(this, uri, source); |
| 74 } else if (extension == '.dart' || uriString.startsWith('dart:')) { | 72 } else if (extension == '.dart' || uriString.startsWith('dart:')) { |
| 75 return new DartSourceNode(uri, source); | 73 return new DartSourceNode(this, uri, source); |
| 76 } else { | 74 } else { |
| 77 return new ResourceSourceNode(uri, source); | 75 return new ResourceSourceNode(this, uri, source); |
| 78 } | 76 } |
| 79 }); | 77 }); |
| 80 } | 78 } |
| 81 } | 79 } |
| 82 | 80 |
| 83 /// A node in the import graph representing a source file. | 81 /// A node in the import graph representing a source file. |
| 84 abstract class SourceNode { | 82 abstract class SourceNode { |
| 83 final SourceGraph graph; |
| 84 |
| 85 /// Resolved URI for this node. | 85 /// Resolved URI for this node. |
| 86 final Uri uri; | 86 final Uri uri; |
| 87 | 87 |
| 88 /// Resolved source from the analyzer. We let the analyzer internally track | 88 /// Resolved source from the analyzer. We let the analyzer internally track |
| 89 /// for modifications to the source files. | 89 /// for modifications to the source files. |
| 90 Source _source; | 90 Source _source; |
| 91 Source get source => _source; | 91 Source get source => _source; |
| 92 | 92 |
| 93 String get contents => graph._context.getContents(source).data; |
| 94 |
| 93 /// Last stamp read from `source.modificationStamp`. | 95 /// Last stamp read from `source.modificationStamp`. |
| 94 int _lastStamp = 0; | 96 int _lastStamp = 0; |
| 95 | 97 |
| 96 /// A hash used to help browsers cache the output that would be produced from | 98 /// A hash used to help browsers cache the output that would be produced from |
| 97 /// building this node. | 99 /// building this node. |
| 98 String cachingHash; | 100 String cachingHash; |
| 99 | 101 |
| 100 /// Whether we need to rebuild this source file. | 102 /// Whether we need to rebuild this source file. |
| 101 bool needsRebuild = false; | 103 bool needsRebuild = false; |
| 102 | 104 |
| 103 /// Whether the structure of dependencies from this node (scripts, imports, | 105 /// Whether the structure of dependencies from this node (scripts, imports, |
| 104 /// exports, or parts) changed after we reparsed its contents. | 106 /// exports, or parts) changed after we reparsed its contents. |
| 105 bool structureChanged = false; | 107 bool structureChanged = false; |
| 106 | 108 |
| 107 /// Direct dependencies in the [SourceGraph]. These include script tags for | 109 /// Direct dependencies in the [SourceGraph]. These include script tags for |
| 108 /// [HtmlSourceNode]s; and imports, exports and parts for [DartSourceNode]s. | 110 /// [HtmlSourceNode]s; and imports, exports and parts for [DartSourceNode]s. |
| 109 Iterable<SourceNode> get allDeps => const []; | 111 Iterable<SourceNode> get allDeps => const []; |
| 110 | 112 |
| 111 /// Like [allDeps] but excludes parts for [DartSourceNode]s. For many | 113 /// Like [allDeps] but excludes parts for [DartSourceNode]s. For many |
| 112 /// operations we mainly care about dependencies at the library level, so | 114 /// operations we mainly care about dependencies at the library level, so |
| 113 /// parts are excluded from this list. | 115 /// parts are excluded from this list. |
| 114 Iterable<SourceNode> get depsWithoutParts => const []; | 116 Iterable<SourceNode> get depsWithoutParts => const []; |
| 115 | 117 |
| 116 SourceNode(this.uri, this._source); | 118 SourceNode(this.graph, this.uri, this._source); |
| 117 | 119 |
| 118 /// Check for whether the file has changed and, if so, mark [needsRebuild] and | 120 /// Check for whether the file has changed and, if so, mark [needsRebuild] and |
| 119 /// [structureChanged] as necessary. | 121 /// [structureChanged] as necessary. |
| 120 void update(SourceGraph graph) { | 122 void update() { |
| 121 if (_source == null) { | 123 if (_source == null) { |
| 122 _source = graph._context.sourceFactory.forUri(Uri.encodeFull('$uri')); | 124 _source = graph._context.sourceFactory.forUri(Uri.encodeFull('$uri')); |
| 123 if (_source == null) return; | 125 if (_source == null) return; |
| 124 } | 126 } |
| 125 int newStamp = _source.modificationStamp; | 127 int newStamp = _source.modificationStamp; |
| 126 if (newStamp > _lastStamp) { | 128 if (newStamp > _lastStamp) { |
| 129 // If the timestamp changed, read the file from disk and cache it. |
| 130 // We don't want the source text to change during compilation. |
| 131 saveUpdatedContents(); |
| 127 _lastStamp = newStamp; | 132 _lastStamp = newStamp; |
| 128 needsRebuild = true; | 133 needsRebuild = true; |
| 129 } | 134 } |
| 130 } | 135 } |
| 131 | 136 |
| 137 void saveUpdatedContents() {} |
| 138 |
| 132 String toString() { | 139 String toString() { |
| 133 var simpleUri = uri.scheme == 'file' ? path.relative(uri.path) : "$uri"; | 140 var simpleUri = uri.scheme == 'file' ? path.relative(uri.path) : "$uri"; |
| 134 return '[$runtimeType: $simpleUri]'; | 141 return '[$runtimeType: $simpleUri]'; |
| 135 } | 142 } |
| 136 } | 143 } |
| 137 | 144 |
| 138 /// A node representing an entry HTML source file. | 145 /// A node representing an entry HTML source file. |
| 139 class HtmlSourceNode extends SourceNode { | 146 class HtmlSourceNode extends SourceNode { |
| 140 /// Resources included by default on any application. | 147 /// Resources included by default on any application. |
| 141 final runtimeDeps; | 148 final runtimeDeps; |
| 142 | 149 |
| 143 /// Libraries referred to via script tags. | 150 /// Libraries referred to via script tags. |
| 144 Set<DartSourceNode> scripts = new Set<DartSourceNode>(); | 151 Set<DartSourceNode> scripts = new Set<DartSourceNode>(); |
| 145 | 152 |
| 146 /// Link-rel stylesheets and images. | 153 /// Link-rel stylesheets and images. |
| 147 Set<ResourceSourceNode> resources = new Set<ResourceSourceNode>(); | 154 Set<ResourceSourceNode> resources = new Set<ResourceSourceNode>(); |
| 148 | 155 |
| 149 @override | 156 @override |
| 150 Iterable<SourceNode> get allDeps => | 157 Iterable<SourceNode> get allDeps => |
| 151 [scripts, resources, runtimeDeps].expand((e) => e); | 158 [scripts, resources, runtimeDeps].expand((e) => e); |
| 152 | 159 |
| 153 @override | 160 @override |
| 154 Iterable<SourceNode> get depsWithoutParts => allDeps; | 161 Iterable<SourceNode> get depsWithoutParts => allDeps; |
| 155 | 162 |
| 156 /// Parsed document, updated whenever [update] is invoked. | 163 /// Parsed document, updated whenever [update] is invoked. |
| 157 Document document; | 164 Document document; |
| 158 | 165 |
| 159 HtmlSourceNode(Uri uri, Source source, SourceGraph graph) | 166 HtmlSourceNode(SourceGraph graph, Uri uri, Source source) |
| 160 : runtimeDeps = graph.runtimeDeps, | 167 : runtimeDeps = graph.runtimeDeps, |
| 161 super(uri, source); | 168 super(graph, uri, source); |
| 162 | 169 |
| 163 void update(SourceGraph graph) { | 170 @override |
| 164 super.update(graph); | 171 void update() { |
| 172 super.update(); |
| 165 if (needsRebuild) { | 173 if (needsRebuild) { |
| 166 graph._reporter.clearHtml(uri); | 174 graph._reporter.clearHtml(uri); |
| 167 document = html.parse(source.contents.data, generateSpans: true); | 175 document = html.parse(contents, generateSpans: true); |
| 168 var newScripts = new Set<DartSourceNode>(); | 176 var newScripts = new Set<DartSourceNode>(); |
| 169 var tags = document.querySelectorAll('script[type="application/dart"]'); | 177 var tags = document.querySelectorAll('script[type="application/dart"]'); |
| 170 for (var script in tags) { | 178 for (var script in tags) { |
| 171 var src = script.attributes['src']; | 179 var src = script.attributes['src']; |
| 172 if (src == null) { | 180 if (src == null) { |
| 173 _reportError(graph, 'inlined script tags not supported at this time ' | 181 _reportError(graph, 'inlined script tags not supported at this time ' |
| 174 '(see https://github.com/dart-lang/dart-dev-compiler/issues/54).', | 182 '(see https://github.com/dart-lang/dart-dev-compiler/issues/54).', |
| 175 script); | 183 script); |
| 176 continue; | 184 continue; |
| 177 } | 185 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 197 } | 205 } |
| 198 if (!_same(newResources, resources)) { | 206 if (!_same(newResources, resources)) { |
| 199 structureChanged = true; | 207 structureChanged = true; |
| 200 resources = newResources; | 208 resources = newResources; |
| 201 } | 209 } |
| 202 } | 210 } |
| 203 } | 211 } |
| 204 | 212 |
| 205 void _reportError(SourceGraph graph, String message, Node node) { | 213 void _reportError(SourceGraph graph, String message, Node node) { |
| 206 graph._reporter.enterHtml(source.uri); | 214 graph._reporter.enterHtml(source.uri); |
| 207 graph._reporter.log(new DependencyGraphError(message, node.sourceSpan)); | 215 var span = node.sourceSpan; |
| 216 graph._reporter.log( |
| 217 new Message(message, Level.SEVERE, span.start.offset, span.end.offset)); |
| 208 graph._reporter.leaveHtml(); | 218 graph._reporter.leaveHtml(); |
| 209 } | 219 } |
| 210 } | 220 } |
| 211 | 221 |
| 212 /// A node representing a Dart library or part. | 222 /// A node representing a Dart library or part. |
| 213 class DartSourceNode extends SourceNode { | 223 class DartSourceNode extends SourceNode { |
| 214 /// Set of imported libraries (empty for part files). | 224 /// Set of imported libraries (empty for part files). |
| 215 Set<DartSourceNode> imports = new Set<DartSourceNode>(); | 225 Set<DartSourceNode> imports = new Set<DartSourceNode>(); |
| 216 | 226 |
| 217 /// Set of exported libraries (empty for part files). | 227 /// Set of exported libraries (empty for part files). |
| 218 Set<DartSourceNode> exports = new Set<DartSourceNode>(); | 228 Set<DartSourceNode> exports = new Set<DartSourceNode>(); |
| 219 | 229 |
| 220 /// Parts of this library (empty for part files). | 230 /// Parts of this library (empty for part files). |
| 221 Set<DartSourceNode> parts = new Set<DartSourceNode>(); | 231 Set<DartSourceNode> parts = new Set<DartSourceNode>(); |
| 222 | 232 |
| 223 /// How many times this file is included as a part. | 233 /// How many times this file is included as a part. |
| 224 int includedAsPart = 0; | 234 int includedAsPart = 0; |
| 225 | 235 |
| 226 DartSourceNode(uri, source) : super(uri, source); | 236 DartSourceNode(graph, uri, source) : super(graph, uri, source); |
| 227 | 237 |
| 228 @override | 238 @override |
| 229 Iterable<SourceNode> get allDeps => | 239 Iterable<SourceNode> get allDeps => |
| 230 [imports, exports, parts].expand((e) => e); | 240 [imports, exports, parts].expand((e) => e); |
| 231 | 241 |
| 232 @override | 242 @override |
| 233 Iterable<SourceNode> get depsWithoutParts => | 243 Iterable<SourceNode> get depsWithoutParts => |
| 234 [imports, exports].expand((e) => e); | 244 [imports, exports].expand((e) => e); |
| 235 | 245 |
| 236 LibraryInfo info; | 246 LibraryInfo info; |
| 237 | 247 |
| 238 void update(SourceGraph graph) { | 248 // TODO(jmesserly): it would be nice to not keep all sources in memory at |
| 239 super.update(graph); | 249 // once, but how else can we ensure a consistent view across a given |
| 250 // compile? One different from dev_compiler vs analyzer is that our |
| 251 // messages later in the compiler need the original source text to print |
| 252 // spans. We also read source text ourselves to parse directives. |
| 253 // But we could discard it after that point. |
| 254 void saveUpdatedContents() { |
| 255 graph._context.setContents(source, source.contents.data); |
| 256 } |
| 240 | 257 |
| 241 if (needsRebuild && source.contents.data != null) { | 258 @override |
| 259 void update() { |
| 260 super.update(); |
| 261 |
| 262 if (needsRebuild && contents != null) { |
| 242 graph._reporter.clearLibrary(uri); | 263 graph._reporter.clearLibrary(uri); |
| 243 // If the defining compilation-unit changed, the structure might have | 264 // If the defining compilation-unit changed, the structure might have |
| 244 // changed. | 265 // changed. |
| 245 var unit = parseDirectives(source.contents.data, name: source.fullName); | 266 var unit = parseDirectives(contents, name: source.fullName); |
| 246 var newImports = new Set<DartSourceNode>(); | 267 var newImports = new Set<DartSourceNode>(); |
| 247 var newExports = new Set<DartSourceNode>(); | 268 var newExports = new Set<DartSourceNode>(); |
| 248 var newParts = new Set<DartSourceNode>(); | 269 var newParts = new Set<DartSourceNode>(); |
| 249 for (var d in unit.directives) { | 270 for (var d in unit.directives) { |
| 250 // Nothing to do for parts. | 271 // Nothing to do for parts. |
| 251 if (d is PartOfDirective) return; | 272 if (d is PartOfDirective) return; |
| 252 if (d is LibraryDirective) continue; | 273 if (d is LibraryDirective) continue; |
| 253 | 274 |
| 254 // `dart:core` and other similar URLs only contain a name, but it is | 275 // `dart:core` and other similar URLs only contain a name, but it is |
| 255 // meant to be a folder when resolving relative paths from it. | 276 // meant to be a folder when resolving relative paths from it. |
| 256 var targetUri = uri.scheme == 'dart' && uri.pathSegments.length == 1 | 277 var targetUri = uri.scheme == 'dart' && uri.pathSegments.length == 1 |
| 257 ? Uri.parse('$uri/').resolve(d.uri.stringValue) | 278 ? Uri.parse('$uri/').resolve(d.uri.stringValue) |
| 258 : uri.resolve(d.uri.stringValue); | 279 : uri.resolve(d.uri.stringValue); |
| 259 var target = | 280 var target = |
| 260 ParseDartTask.resolveDirective(graph._context, source, d, null); | 281 ParseDartTask.resolveDirective(graph._context, source, d, null); |
| 261 if (target != null) { | 282 if (target != null) { |
| 262 if (targetUri != target.uri) print(">> ${target.uri} $targetUri"); | 283 if (targetUri != target.uri) print(">> ${target.uri} $targetUri"); |
| 263 } | 284 } |
| 264 var node = graph.nodes.putIfAbsent( | 285 var node = graph.nodes.putIfAbsent( |
| 265 targetUri, () => new DartSourceNode(targetUri, target)); | 286 targetUri, () => new DartSourceNode(graph, targetUri, target)); |
| 266 //var node = graph.nodeFromUri(targetUri); | 287 //var node = graph.nodeFromUri(targetUri); |
| 267 if (node.source == null || !node.source.exists()) { | 288 if (node.source == null || !node.source.exists()) { |
| 268 _reportError(graph, 'File $targetUri not found', unit, d); | 289 _reportError(graph, 'File $targetUri not found', unit, d); |
| 269 } | 290 } |
| 270 | 291 |
| 271 if (d is ImportDirective) { | 292 if (d is ImportDirective) { |
| 272 newImports.add(node); | 293 newImports.add(node); |
| 273 } else if (d is ExportDirective) { | 294 } else if (d is ExportDirective) { |
| 274 newExports.add(node); | 295 newExports.add(node); |
| 275 } else if (d is PartDirective) { | 296 } else if (d is PartDirective) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 307 } | 328 } |
| 308 } | 329 } |
| 309 | 330 |
| 310 // The library should be marked as needing rebuild if a part changed | 331 // The library should be marked as needing rebuild if a part changed |
| 311 // internally: | 332 // internally: |
| 312 for (var p in parts) { | 333 for (var p in parts) { |
| 313 // Technically for parts we don't need to look at the contents. If they | 334 // Technically for parts we don't need to look at the contents. If they |
| 314 // contain imports, exports, or parts, we'll ignore them in our crawling. | 335 // contain imports, exports, or parts, we'll ignore them in our crawling. |
| 315 // However we do a full update to make it easier to adjust when users | 336 // However we do a full update to make it easier to adjust when users |
| 316 // switch a file from a part to a library. | 337 // switch a file from a part to a library. |
| 317 p.update(graph); | 338 p.update(); |
| 318 if (p.needsRebuild) needsRebuild = true; | 339 if (p.needsRebuild) needsRebuild = true; |
| 319 } | 340 } |
| 320 } | 341 } |
| 321 | 342 |
| 322 void _reportError( | 343 void _reportError( |
| 323 SourceGraph graph, String message, CompilationUnit unit, AstNode node) { | 344 SourceGraph graph, String message, CompilationUnit unit, AstNode node) { |
| 324 graph._reporter.enterLibrary(source.uri); | 345 graph._reporter |
| 325 graph._reporter.log( | 346 ..enterLibrary(source.uri) |
| 326 new DependencyGraphError(message, spanForNode(unit, source, node))); | 347 ..enterCompilationUnit(unit, source) |
| 327 graph._reporter.leaveLibrary(); | 348 ..log(new Message(message, Level.SEVERE, node.offset, node.end)) |
| 349 ..leaveCompilationUnit() |
| 350 ..leaveLibrary(); |
| 328 } | 351 } |
| 329 } | 352 } |
| 330 | 353 |
| 331 /// Represents a runtime resource from our compiler that is needed to run an | 354 /// Represents a runtime resource from our compiler that is needed to run an |
| 332 /// application. | 355 /// application. |
| 333 class ResourceSourceNode extends SourceNode { | 356 class ResourceSourceNode extends SourceNode { |
| 334 ResourceSourceNode(uri, source) : super(uri, source); | 357 ResourceSourceNode(graph, uri, source) : super(graph, uri, source); |
| 335 } | 358 } |
| 336 | 359 |
| 337 /// Updates the structure and `needsRebuild` marks in nodes of [graph] reachable | 360 /// Updates the structure and `needsRebuild` marks in nodes of [graph] reachable |
| 338 /// from [start]. | 361 /// from [start]. |
| 339 /// | 362 /// |
| 340 /// That is, staring from [start], we update the graph by detecting file changes | 363 /// That is, staring from [start], we update the graph by detecting file changes |
| 341 /// and rebuilding the structure of the graph wherever it changed (an import was | 364 /// and rebuilding the structure of the graph wherever it changed (an import was |
| 342 /// added or removed, etc). | 365 /// added or removed, etc). |
| 343 /// | 366 /// |
| 344 /// After calling this function a node is marked with `needsRebuild` only if it | 367 /// After calling this function a node is marked with `needsRebuild` only if it |
| 345 /// contained local changes. Rebuild decisions that derive from transitive | 368 /// contained local changes. Rebuild decisions that derive from transitive |
| 346 /// changes (e.g. when the API of a dependency changed) are handled later in | 369 /// changes (e.g. when the API of a dependency changed) are handled later in |
| 347 /// [rebuild]. | 370 /// [rebuild]. |
| 348 void refreshStructureAndMarks(SourceNode start, SourceGraph graph) { | 371 void refreshStructureAndMarks(SourceNode start) { |
| 349 visitInPreOrder(start, (n) => n.update(graph), includeParts: false); | 372 visitInPreOrder(start, (n) => n.update(), includeParts: false); |
| 350 } | 373 } |
| 351 | 374 |
| 352 /// Clears all the `needsRebuild` and `structureChanged` marks in nodes | 375 /// Clears all the `needsRebuild` and `structureChanged` marks in nodes |
| 353 /// reachable from [start]. | 376 /// reachable from [start]. |
| 354 void clearMarks(SourceNode start) { | 377 void clearMarks(SourceNode start) { |
| 355 visitInPreOrder(start, (n) => n.needsRebuild = n.structureChanged = false, | 378 visitInPreOrder(start, (n) => n.needsRebuild = n.structureChanged = false, |
| 356 includeParts: true); | 379 includeParts: true); |
| 357 } | 380 } |
| 358 | 381 |
| 359 /// Traverses from [start] with the purpose of building any source that needs to | 382 /// Traverses from [start] with the purpose of building any source that needs to |
| (...skipping 15 matching lines...) Expand all Loading... |
| 375 /// | 398 /// |
| 376 /// * Rebuild [HtmlSourceNode]s if there were structural changes somewhere | 399 /// * Rebuild [HtmlSourceNode]s if there were structural changes somewhere |
| 377 /// down its reachable subgraph. This is done because HTML files embed the | 400 /// down its reachable subgraph. This is done because HTML files embed the |
| 378 /// transitive closure of the import graph in their output. | 401 /// transitive closure of the import graph in their output. |
| 379 /// | 402 /// |
| 380 /// * Rebuild [DartSourceNode]s that depend on other [DartSourceNode]s | 403 /// * Rebuild [DartSourceNode]s that depend on other [DartSourceNode]s |
| 381 /// whose API may have changed. The result of [build] is used to determine | 404 /// whose API may have changed. The result of [build] is used to determine |
| 382 /// whether other nodes need to be rebuilt. The function [build] is expected | 405 /// whether other nodes need to be rebuilt. The function [build] is expected |
| 383 /// to return `true` on a node `n` if it detemines other nodes that import | 406 /// to return `true` on a node `n` if it detemines other nodes that import |
| 384 /// `n` may need to be rebuilt as well. | 407 /// `n` may need to be rebuilt as well. |
| 385 rebuild(SourceNode start, SourceGraph graph, bool build(SourceNode node)) { | 408 rebuild(SourceNode start, bool build(SourceNode node)) { |
| 386 refreshStructureAndMarks(start, graph); | 409 refreshStructureAndMarks(start); |
| 387 // Hold which source nodes may have changed their public API, this includes | 410 // Hold which source nodes may have changed their public API, this includes |
| 388 // libraries that were modified or libraries that export other modified APIs. | 411 // libraries that were modified or libraries that export other modified APIs. |
| 389 // TODO(sigmund): consider removing this special support for exports? Many | 412 // TODO(sigmund): consider removing this special support for exports? Many |
| 390 // cases anways require using summaries to understand what parts of the public | 413 // cases anways require using summaries to understand what parts of the public |
| 391 // API may be affected by transitive changes. The re-export case is just one | 414 // API may be affected by transitive changes. The re-export case is just one |
| 392 // of those transitive cases, but is not sufficient. See | 415 // of those transitive cases, but is not sufficient. See |
| 393 // https://github.com/dart-lang/dev_compiler/issues/76 | 416 // https://github.com/dart-lang/dev_compiler/issues/76 |
| 394 var apiChangeDetected = new HashSet<SourceNode>(); | 417 var apiChangeDetected = new HashSet<SourceNode>(); |
| 395 bool htmlNeedsRebuild = false; | 418 bool htmlNeedsRebuild = false; |
| 396 | 419 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 447 if (!seen.add(node)) return; | 470 if (!seen.add(node)) return; |
| 448 var deps = includeParts ? node.allDeps : node.depsWithoutParts; | 471 var deps = includeParts ? node.allDeps : node.depsWithoutParts; |
| 449 deps.forEach(helper); | 472 deps.forEach(helper); |
| 450 action(node); | 473 action(node); |
| 451 } | 474 } |
| 452 helper(start); | 475 helper(start); |
| 453 } | 476 } |
| 454 | 477 |
| 455 bool _same(Set a, Set b) => a.length == b.length && a.containsAll(b); | 478 bool _same(Set a, Set b) => a.length == b.length && a.containsAll(b); |
| 456 | 479 |
| 457 /// An error message discovered while parsing the dependencies between files. | |
| 458 class DependencyGraphError extends MessageWithSpan { | |
| 459 const DependencyGraphError(String message, SourceSpan span) | |
| 460 : super(message, Level.SEVERE, span); | |
| 461 } | |
| 462 | |
| 463 /// Runtime files added to all applications when running the compiler in the | 480 /// Runtime files added to all applications when running the compiler in the |
| 464 /// command line. | 481 /// command line. |
| 465 final defaultRuntimeFiles = () { | 482 final defaultRuntimeFiles = () { |
| 466 var files = ['harmony_feature_check.js', 'dart_runtime.js',]; | 483 var files = ['harmony_feature_check.js', 'dart_runtime.js',]; |
| 467 files.addAll(corelibOrder.map((l) => l.replaceAll('.', '/') + '.js')); | 484 files.addAll(corelibOrder.map((l) => l.replaceAll('.', '/') + '.js')); |
| 468 return files; | 485 return files; |
| 469 }(); | 486 }(); |
| 470 | 487 |
| 471 /// Curated order to minimize lazy classes needed by dart:core and its | 488 /// Curated order to minimize lazy classes needed by dart:core and its |
| 472 /// transitive SDK imports. | 489 /// transitive SDK imports. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 492 'dart._interceptors', | 509 'dart._interceptors', |
| 493 'dart._native_typed_data', | 510 'dart._native_typed_data', |
| 494 */ | 511 */ |
| 495 ]; | 512 ]; |
| 496 | 513 |
| 497 /// Runtime files added to applications when running in server mode. | 514 /// Runtime files added to applications when running in server mode. |
| 498 final runtimeFilesForServerMode = new List<String>.from(defaultRuntimeFiles) | 515 final runtimeFilesForServerMode = new List<String>.from(defaultRuntimeFiles) |
| 499 ..addAll(const ['messages_widget.js', 'messages.css']); | 516 ..addAll(const ['messages_widget.js', 'messages.css']); |
| 500 | 517 |
| 501 final _log = new Logger('dev_compiler.dependency_graph'); | 518 final _log = new Logger('dev_compiler.dependency_graph'); |
| OLD | NEW |