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

Side by Side Diff: lib/src/dependency_graph.dart

Issue 993213003: Support browser caching using hashes in serverMode (fixes #93, fixes #92) (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 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 | « lib/src/codegen/js_codegen.dart ('k') | lib/src/utils.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 LibraryDirective, 13 LibraryDirective,
14 ImportDirective, 14 ImportDirective,
15 ExportDirective, 15 ExportDirective,
16 PartDirective, 16 PartDirective,
17 PartOfDirective, 17 PartOfDirective,
18 CompilationUnit, 18 CompilationUnit,
19 Identifier; 19 Identifier;
20 import 'package:analyzer/src/generated/engine.dart' 20 import 'package:analyzer/src/generated/engine.dart'
21 show ParseDartTask, AnalysisContext; 21 show ParseDartTask, AnalysisContext;
22 import 'package:analyzer/src/generated/source.dart' show Source, SourceKind; 22 import 'package:analyzer/src/generated/source.dart' show Source, SourceKind;
23 import 'package:html5lib/dom.dart' show Document; 23 import 'package:html5lib/dom.dart' show Document;
24 import 'package:html5lib/parser.dart' as html; 24 import 'package:html5lib/parser.dart' as html;
25 import 'package:logging/logging.dart' show Level; 25 import 'package:logging/logging.dart' show Level;
26 import 'package:path/path.dart' as path; 26 import 'package:path/path.dart' as path;
27 import 'package:source_span/source_span.dart' show SourceSpan; 27 import 'package:source_span/source_span.dart' show SourceSpan;
28 28
29 import 'info.dart'; 29 import 'info.dart';
30 import 'options.dart';
30 import 'report.dart'; 31 import 'report.dart';
31 import 'utils.dart'; 32 import 'utils.dart';
32 33
33 /// Holds references to all source nodes in the import graph. This is mainly 34 /// Holds references to all source nodes in the import graph. This is mainly
34 /// used as a level of indirection to ensure that each source has a canonical 35 /// used as a level of indirection to ensure that each source has a canonical
35 /// representation. 36 /// representation.
36 class SourceGraph { 37 class SourceGraph {
37 /// All nodes in the source graph. Used to get a canonical representation for 38 /// All nodes in the source graph. Used to get a canonical representation for
38 /// any node. 39 /// any node.
39 final Map<Uri, SourceNode> nodes = {}; 40 final Map<Uri, SourceNode> nodes = {};
40 41
41 /// Analyzer used to resolve source files. 42 /// Analyzer used to resolve source files.
42 final AnalysisContext _context; 43 final AnalysisContext _context;
43 final CheckerReporter _reporter; 44 final CheckerReporter _reporter;
45 final CompilerOptions _options;
44 46
45 SourceGraph(this._context, this._reporter); 47 SourceGraph(this._context, this._reporter, this._options);
46 48
47 /// Node associated with a resolved [uri]. 49 /// Node associated with a resolved [uri].
48 SourceNode nodeFromUri(Uri uri) { 50 SourceNode nodeFromUri(Uri uri) {
49 var uriString = Uri.encodeFull('$uri'); 51 var uriString = Uri.encodeFull('$uri');
50 return nodes.putIfAbsent(uri, () { 52 return nodes.putIfAbsent(uri, () {
51 var source = _context.sourceFactory.forUri(uriString); 53 var source = _context.sourceFactory.forUri(uriString);
52 var extension = path.extension(uriString); 54 var extension = path.extension(uriString);
53 if (extension == '.html') { 55 if (extension == '.html') {
54 return new HtmlSourceNode(uri, source, this); 56 return new HtmlSourceNode(uri, source, this);
55 } else if (extension == '.dart' || uriString.startsWith('dart:')) { 57 } else if (extension == '.dart' || uriString.startsWith('dart:')) {
(...skipping 12 matching lines...) Expand all
68 /// Resolved URI for this node. 70 /// Resolved URI for this node.
69 final Uri uri; 71 final Uri uri;
70 72
71 /// Resolved source from the analyzer. We let the analyzer internally track 73 /// Resolved source from the analyzer. We let the analyzer internally track
72 /// for modifications to the source files. 74 /// for modifications to the source files.
73 final Source source; 75 final Source source;
74 76
75 /// Last stamp read from `source.modificationStamp`. 77 /// Last stamp read from `source.modificationStamp`.
76 int _lastStamp = 0; 78 int _lastStamp = 0;
77 79
80 /// A hash used to help browsers cache the output that would be produced from
81 /// building this node.
82 String cachingHash;
83
78 /// Whether we need to rebuild this source file. 84 /// Whether we need to rebuild this source file.
79 bool needsRebuild = false; 85 bool needsRebuild = false;
80 86
81 /// Whether the structure of dependencies from this node (scripts, imports, 87 /// Whether the structure of dependencies from this node (scripts, imports,
82 /// exports, or parts) changed after we reparsed its contents. 88 /// exports, or parts) changed after we reparsed its contents.
83 bool structureChanged = false; 89 bool structureChanged = false;
84 90
85 /// Direct dependencies in the [SourceGraph]. These include script tags for 91 /// Direct dependencies in the [SourceGraph]. These include script tags for
86 /// [HtmlSourceNode]s; and imports, exports and parts for [DartSourceNode]s. 92 /// [HtmlSourceNode]s; and imports, exports and parts for [DartSourceNode]s.
87 Iterable<SourceNode> get allDeps => const []; 93 Iterable<SourceNode> get allDeps => const [];
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
121 Iterable<SourceNode> get allDeps => [scripts, runtimeDeps].expand((e) => e); 127 Iterable<SourceNode> get allDeps => [scripts, runtimeDeps].expand((e) => e);
122 128
123 @override 129 @override
124 Iterable<SourceNode> get depsWithoutParts => allDeps; 130 Iterable<SourceNode> get depsWithoutParts => allDeps;
125 131
126 /// Parsed document, updated whenever [update] is invoked. 132 /// Parsed document, updated whenever [update] is invoked.
127 Document document; 133 Document document;
128 134
129 HtmlSourceNode(uri, source, graph) : super(uri, source) { 135 HtmlSourceNode(uri, source, graph) : super(uri, source) {
130 var prefix = 'package:dev_compiler/runtime'; 136 var prefix = 'package:dev_compiler/runtime';
131 runtimeDeps 137 var files = ['harmony_feature_check.js', 'dart_runtime.js'];
132 ..add(graph.nodeFromUri(Uri.parse('$prefix/dart_runtime.js'))) 138 if (graph._options.serverMode) {
133 ..add(graph.nodeFromUri(Uri.parse('$prefix/harmony_feature_check.js'))) 139 files.addAll(const ['messages_widget.js', 'messages.css']);
134 ..add(graph.nodeFromUri(Uri.parse('$prefix/messages_widget.js'))) 140 }
135 ..add(graph.nodeFromUri(Uri.parse('$prefix/messages.css'))); 141 files.forEach((file) {
142 runtimeDeps.add(graph.nodeFromUri(Uri.parse('$prefix/$file')));
143 });
136 } 144 }
137 145
138 void update(SourceGraph graph) { 146 void update(SourceGraph graph) {
139 super.update(graph); 147 super.update(graph);
140 if (needsRebuild) { 148 if (needsRebuild) {
141 graph._reporter.clearHtml(uri); 149 graph._reporter.clearHtml(uri);
142 document = html.parse(source.contents.data, generateSpans: true); 150 document = html.parse(source.contents.data, generateSpans: true);
143 var newScripts = new Set<DartSourceNode>(); 151 var newScripts = new Set<DartSourceNode>();
144 var tags = document.querySelectorAll('script[type="application/dart"]'); 152 var tags = document.querySelectorAll('script[type="application/dart"]');
145 for (var script in tags) { 153 for (var script in tags) {
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
333 rebuild(SourceNode start, SourceGraph graph, bool build(SourceNode node)) { 341 rebuild(SourceNode start, SourceGraph graph, bool build(SourceNode node)) {
334 refreshStructureAndMarks(start, graph); 342 refreshStructureAndMarks(start, graph);
335 // Hold which source nodes may have changed their public API, this includes 343 // Hold which source nodes may have changed their public API, this includes
336 // libraries that were modified or libraries that export other modified APIs. 344 // libraries that were modified or libraries that export other modified APIs.
337 // TODO(sigmund): consider removing this special support for exports? Many 345 // TODO(sigmund): consider removing this special support for exports? Many
338 // cases anways require using summaries to understand what parts of the public 346 // cases anways require using summaries to understand what parts of the public
339 // API may be affected by transitive changes. The re-export case is just one 347 // API may be affected by transitive changes. The re-export case is just one
340 // of those transitive cases, but is not sufficient. See 348 // of those transitive cases, but is not sufficient. See
341 // https://github.com/dart-lang/dev_compiler/issues/76 349 // https://github.com/dart-lang/dev_compiler/issues/76
342 var apiChangeDetected = new HashSet<SourceNode>(); 350 var apiChangeDetected = new HashSet<SourceNode>();
343 bool structureHasChanged = false; 351 bool htmlNeedsRebuild = false;
344 352
345 bool shouldBuildNode(SourceNode n) { 353 bool shouldBuildNode(SourceNode n) {
346 if (n.needsRebuild) return true; 354 if (n.needsRebuild) return true;
347 if (n is HtmlSourceNode) return structureHasChanged; 355 if (n is HtmlSourceNode) return htmlNeedsRebuild;
348 if (n is ResourceSourceNode) return false; 356 if (n is ResourceSourceNode) return false;
349 return (n as DartSourceNode).imports 357 return (n as DartSourceNode).imports
350 .any((i) => apiChangeDetected.contains(i)); 358 .any((i) => apiChangeDetected.contains(i));
351 } 359 }
352 360
353 visitInPostOrder(start, (n) { 361 visitInPostOrder(start, (n) {
354 if (n.structureChanged) structureHasChanged = true; 362 if (n.structureChanged) htmlNeedsRebuild = true;
355 if (shouldBuildNode(n)) { 363 if (shouldBuildNode(n)) {
364 var oldHash = n.cachingHash;
356 if (build(n)) apiChangeDetected.add(n); 365 if (build(n)) apiChangeDetected.add(n);
366 if (oldHash != n.cachingHash) htmlNeedsRebuild = true;
357 } else if (n is DartSourceNode && 367 } else if (n is DartSourceNode &&
358 n.exports.any((e) => apiChangeDetected.contains(e))) { 368 n.exports.any((e) => apiChangeDetected.contains(e))) {
359 apiChangeDetected.add(n); 369 apiChangeDetected.add(n);
360 } 370 }
361 n.needsRebuild = false; 371 n.needsRebuild = false;
362 n.structureChanged = false; 372 n.structureChanged = false;
363 if (n is DartSourceNode) { 373 if (n is DartSourceNode) {
364 // Note: clearing out flags in the parts could be a problem if someone 374 // Note: clearing out flags in the parts could be a problem if someone
365 // tries to use a file both as a part and a library at the same time. 375 // tries to use a file both as a part and a library at the same time.
366 // In that case, we might not correctly propagate changes in the places 376 // In that case, we might not correctly propagate changes in the places
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 helper(start); 408 helper(start);
399 } 409 }
400 410
401 bool _same(Set a, Set b) => a.length == b.length && a.containsAll(b); 411 bool _same(Set a, Set b) => a.length == b.length && a.containsAll(b);
402 412
403 /// An error message discovered while parsing the dependencies between files. 413 /// An error message discovered while parsing the dependencies between files.
404 class DependencyGraphError extends MessageWithSpan { 414 class DependencyGraphError extends MessageWithSpan {
405 const DependencyGraphError(String message, SourceSpan span) 415 const DependencyGraphError(String message, SourceSpan span)
406 : super(message, Level.SEVERE, span); 416 : super(message, Level.SEVERE, span);
407 } 417 }
OLDNEW
« no previous file with comments | « lib/src/codegen/js_codegen.dart ('k') | lib/src/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698