Index: lib/src/dependency_graph.dart |
diff --git a/lib/src/dependency_graph.dart b/lib/src/dependency_graph.dart |
index dcded66942caf4a90baf757f77e1483f85cf5e30..dcf876d41257013d6f2d8589f5abb24f691d682e 100644 |
--- a/lib/src/dependency_graph.dart |
+++ b/lib/src/dependency_graph.dart |
@@ -25,12 +25,10 @@ import 'package:html/dom.dart' show Document, Node; |
import 'package:html/parser.dart' as html; |
import 'package:logging/logging.dart' show Logger, Level; |
import 'package:path/path.dart' as path; |
-import 'package:source_span/source_span.dart' show SourceSpan; |
import 'info.dart'; |
import 'options.dart'; |
import 'report.dart'; |
-import 'utils.dart'; |
/// Holds references to all source nodes in the import graph. This is mainly |
/// used as a level of indirection to ensure that each source has a canonical |
@@ -70,11 +68,11 @@ class SourceGraph { |
var source = _context.sourceFactory.forUri(uriString); |
var extension = path.extension(uriString); |
if (extension == '.html') { |
- return new HtmlSourceNode(uri, source, this); |
+ return new HtmlSourceNode(this, uri, source); |
} else if (extension == '.dart' || uriString.startsWith('dart:')) { |
- return new DartSourceNode(uri, source); |
+ return new DartSourceNode(this, uri, source); |
} else { |
- return new ResourceSourceNode(uri, source); |
+ return new ResourceSourceNode(this, uri, source); |
} |
}); |
} |
@@ -82,6 +80,8 @@ class SourceGraph { |
/// A node in the import graph representing a source file. |
abstract class SourceNode { |
+ final SourceGraph graph; |
+ |
/// Resolved URI for this node. |
final Uri uri; |
@@ -90,6 +90,8 @@ abstract class SourceNode { |
Source _source; |
Source get source => _source; |
+ String get contents => graph._context.getContents(source).data; |
+ |
/// Last stamp read from `source.modificationStamp`. |
int _lastStamp = 0; |
@@ -113,22 +115,27 @@ abstract class SourceNode { |
/// parts are excluded from this list. |
Iterable<SourceNode> get depsWithoutParts => const []; |
- SourceNode(this.uri, this._source); |
+ SourceNode(this.graph, this.uri, this._source); |
/// Check for whether the file has changed and, if so, mark [needsRebuild] and |
/// [structureChanged] as necessary. |
- void update(SourceGraph graph) { |
+ void update() { |
if (_source == null) { |
_source = graph._context.sourceFactory.forUri(Uri.encodeFull('$uri')); |
if (_source == null) return; |
} |
int newStamp = _source.modificationStamp; |
if (newStamp > _lastStamp) { |
+ // If the timestamp changed, read the file from disk and cache it. |
+ // We don't want the source text to change during compilation. |
+ saveUpdatedContents(); |
_lastStamp = newStamp; |
needsRebuild = true; |
} |
} |
+ void saveUpdatedContents() {} |
+ |
String toString() { |
var simpleUri = uri.scheme == 'file' ? path.relative(uri.path) : "$uri"; |
return '[$runtimeType: $simpleUri]'; |
@@ -156,15 +163,16 @@ class HtmlSourceNode extends SourceNode { |
/// Parsed document, updated whenever [update] is invoked. |
Document document; |
- HtmlSourceNode(Uri uri, Source source, SourceGraph graph) |
+ HtmlSourceNode(SourceGraph graph, Uri uri, Source source) |
: runtimeDeps = graph.runtimeDeps, |
- super(uri, source); |
+ super(graph, uri, source); |
- void update(SourceGraph graph) { |
- super.update(graph); |
+ @override |
+ void update() { |
+ super.update(); |
if (needsRebuild) { |
graph._reporter.clearHtml(uri); |
- document = html.parse(source.contents.data, generateSpans: true); |
+ document = html.parse(contents, generateSpans: true); |
var newScripts = new Set<DartSourceNode>(); |
var tags = document.querySelectorAll('script[type="application/dart"]'); |
for (var script in tags) { |
@@ -204,7 +212,9 @@ class HtmlSourceNode extends SourceNode { |
void _reportError(SourceGraph graph, String message, Node node) { |
graph._reporter.enterHtml(source.uri); |
- graph._reporter.log(new DependencyGraphError(message, node.sourceSpan)); |
+ var span = node.sourceSpan; |
+ graph._reporter.log( |
+ new Message(message, Level.SEVERE, span.start.offset, span.end.offset)); |
graph._reporter.leaveHtml(); |
} |
} |
@@ -223,7 +233,7 @@ class DartSourceNode extends SourceNode { |
/// How many times this file is included as a part. |
int includedAsPart = 0; |
- DartSourceNode(uri, source) : super(uri, source); |
+ DartSourceNode(graph, uri, source) : super(graph, uri, source); |
@override |
Iterable<SourceNode> get allDeps => |
@@ -235,14 +245,25 @@ class DartSourceNode extends SourceNode { |
LibraryInfo info; |
- void update(SourceGraph graph) { |
- super.update(graph); |
+ // TODO(jmesserly): it would be nice to not keep all sources in memory at |
+ // once, but how else can we ensure a consistent view across a given |
+ // compile? One different from dev_compiler vs analyzer is that our |
+ // messages later in the compiler need the original source text to print |
+ // spans. We also read source text ourselves to parse directives. |
+ // But we could discard it after that point. |
+ void saveUpdatedContents() { |
+ graph._context.setContents(source, source.contents.data); |
+ } |
- if (needsRebuild && source.contents.data != null) { |
+ @override |
+ void update() { |
+ super.update(); |
+ |
+ if (needsRebuild && contents != null) { |
graph._reporter.clearLibrary(uri); |
// If the defining compilation-unit changed, the structure might have |
// changed. |
- var unit = parseDirectives(source.contents.data, name: source.fullName); |
+ var unit = parseDirectives(contents, name: source.fullName); |
var newImports = new Set<DartSourceNode>(); |
var newExports = new Set<DartSourceNode>(); |
var newParts = new Set<DartSourceNode>(); |
@@ -262,7 +283,7 @@ class DartSourceNode extends SourceNode { |
if (targetUri != target.uri) print(">> ${target.uri} $targetUri"); |
} |
var node = graph.nodes.putIfAbsent( |
- targetUri, () => new DartSourceNode(targetUri, target)); |
+ targetUri, () => new DartSourceNode(graph, targetUri, target)); |
//var node = graph.nodeFromUri(targetUri); |
if (node.source == null || !node.source.exists()) { |
_reportError(graph, 'File $targetUri not found', unit, d); |
@@ -314,24 +335,26 @@ class DartSourceNode extends SourceNode { |
// contain imports, exports, or parts, we'll ignore them in our crawling. |
// However we do a full update to make it easier to adjust when users |
// switch a file from a part to a library. |
- p.update(graph); |
+ p.update(); |
if (p.needsRebuild) needsRebuild = true; |
} |
} |
void _reportError( |
SourceGraph graph, String message, CompilationUnit unit, AstNode node) { |
- graph._reporter.enterLibrary(source.uri); |
- graph._reporter.log( |
- new DependencyGraphError(message, spanForNode(unit, source, node))); |
- graph._reporter.leaveLibrary(); |
+ graph._reporter |
+ ..enterLibrary(source.uri) |
+ ..enterCompilationUnit(unit, source) |
+ ..log(new Message(message, Level.SEVERE, node.offset, node.end)) |
+ ..leaveCompilationUnit() |
+ ..leaveLibrary(); |
} |
} |
/// Represents a runtime resource from our compiler that is needed to run an |
/// application. |
class ResourceSourceNode extends SourceNode { |
- ResourceSourceNode(uri, source) : super(uri, source); |
+ ResourceSourceNode(graph, uri, source) : super(graph, uri, source); |
} |
/// Updates the structure and `needsRebuild` marks in nodes of [graph] reachable |
@@ -345,8 +368,8 @@ class ResourceSourceNode extends SourceNode { |
/// contained local changes. Rebuild decisions that derive from transitive |
/// changes (e.g. when the API of a dependency changed) are handled later in |
/// [rebuild]. |
-void refreshStructureAndMarks(SourceNode start, SourceGraph graph) { |
- visitInPreOrder(start, (n) => n.update(graph), includeParts: false); |
+void refreshStructureAndMarks(SourceNode start) { |
+ visitInPreOrder(start, (n) => n.update(), includeParts: false); |
} |
/// Clears all the `needsRebuild` and `structureChanged` marks in nodes |
@@ -382,8 +405,8 @@ void clearMarks(SourceNode start) { |
/// whether other nodes need to be rebuilt. The function [build] is expected |
/// to return `true` on a node `n` if it detemines other nodes that import |
/// `n` may need to be rebuilt as well. |
-rebuild(SourceNode start, SourceGraph graph, bool build(SourceNode node)) { |
- refreshStructureAndMarks(start, graph); |
+rebuild(SourceNode start, bool build(SourceNode node)) { |
+ refreshStructureAndMarks(start); |
// Hold which source nodes may have changed their public API, this includes |
// libraries that were modified or libraries that export other modified APIs. |
// TODO(sigmund): consider removing this special support for exports? Many |
@@ -454,12 +477,6 @@ visitInPostOrder(SourceNode start, void action(SourceNode node), |
bool _same(Set a, Set b) => a.length == b.length && a.containsAll(b); |
-/// An error message discovered while parsing the dependencies between files. |
-class DependencyGraphError extends MessageWithSpan { |
- const DependencyGraphError(String message, SourceSpan span) |
- : super(message, Level.SEVERE, span); |
-} |
- |
/// Runtime files added to all applications when running the compiler in the |
/// command line. |
final defaultRuntimeFiles = () { |