Skip to content

Commit f728691

Browse files
author
John Messerly
committed
Fixes #179 -- compile error if editing files during server mode
this avoids reloading file contents many times during a compile. `.contents.data` == bad! also fixes #73, using Analyzer's LineInfo instead of FileSpan to avoid finding line breaks over and over I did some manual testing on sunflower as well as diffing SDK messages before and after The one baseline change (23 -> 22) seems more accurate than it was before. [email protected] Review URL: https://codereview.chromium.org/1141013002
1 parent 995e0f0 commit f728691

15 files changed

+400
-353
lines changed

pkg/dev_compiler/bin/edit_files.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ library dev_compiler.bin.edit_files;
1313
import 'dart:io';
1414
import 'dart:convert';
1515

16+
import 'package:analyzer/src/generated/source.dart' show Source;
1617
import 'package:args/args.dart';
1718
import 'package:cli_util/cli_util.dart' show getSdkDir;
1819
import 'package:source_maps/refactor.dart';
@@ -63,6 +64,8 @@ class EditFileSummaryVisitor extends RecursiveSummaryVisitor {
6364
RegExp includePattern;
6465
RegExp excludePattern;
6566

67+
final Map<Uri, Source> _sources = <Uri, Source>{};
68+
6669
EditFileSummaryVisitor(this.typeResolver, this.level,
6770
this.checkoutFilesExecutable, this.checkoutFilesArg, this.includePattern,
6871
this.excludePattern);
@@ -72,6 +75,13 @@ class EditFileSummaryVisitor extends RecursiveSummaryVisitor {
7275
return new TextEditTransaction(fileContents, new SourceFile(fileContents));
7376
});
7477

78+
/// Find the corresponding [Source] for [uri].
79+
Source findSource(Uri uri) {
80+
var source = _sources[uri];
81+
if (source != null) return source;
82+
return _sources[uri] = typeResolver.context.sourceFactory.forUri('$uri');
83+
}
84+
7585
@override
7686
void visitMessage(MessageSummary message) {
7787
var uri = message.span.sourceUrl;
@@ -88,7 +98,7 @@ class EditFileSummaryVisitor extends RecursiveSummaryVisitor {
8898
break;
8999
}
90100
}
91-
var fullName = typeResolver.findSource(uri).fullName;
101+
var fullName = findSource(uri).fullName;
92102
if (includePattern != null && !includePattern.hasMatch(fullName)) return;
93103
if (excludePattern != null && excludePattern.hasMatch(fullName)) return;
94104
var edits = getEdits(fullName);

pkg/dev_compiler/lib/devc.dart

Lines changed: 78 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import 'dart:async';
99
import 'dart:convert';
1010
import 'dart:io';
1111

12-
import 'package:analyzer/src/generated/engine.dart' show ChangeSet;
12+
import 'package:analyzer/src/generated/error.dart' as analyzer;
13+
import 'package:analyzer/src/generated/engine.dart'
14+
show AnalysisContext, ChangeSet;
15+
import 'package:analyzer/src/generated/source.dart' show Source;
1316
import 'package:logging/logging.dart' show Level, Logger, LogRecord;
1417
import 'package:path/path.dart' as path;
1518
import 'package:shelf/shelf.dart' as shelf;
@@ -25,7 +28,8 @@ import 'src/codegen/dart_codegen.dart';
2528
import 'src/codegen/html_codegen.dart';
2629
import 'src/codegen/js_codegen.dart';
2730
import 'src/dependency_graph.dart';
28-
import 'src/info.dart' show LibraryInfo, CheckerResults, LibraryUnit;
31+
import 'src/info.dart'
32+
show AnalyzerError, CheckerResults, LibraryInfo, LibraryUnit;
2933
import 'src/options.dart';
3034
import 'src/report.dart';
3135
import 'src/utils.dart';
@@ -39,19 +43,25 @@ StreamSubscription setupLogger(Level level, printFn) {
3943
});
4044
}
4145

46+
abstract class AbstractCompiler {
47+
CompilerOptions get options;
48+
AnalysisContext get context;
49+
TypeRules get rules;
50+
Uri get entryPointUri;
51+
}
52+
4253
/// Encapsulates the logic to do a one-off compilation or a partial compilation
4354
/// when the compiler is run as a development server.
44-
class Compiler {
45-
final CompilerOptions _options;
46-
final TypeResolver _resolver;
55+
class Compiler implements AbstractCompiler {
56+
final CompilerOptions options;
57+
final AnalysisContext context;
4758
final CheckerReporter _reporter;
48-
final TypeRules _rules;
59+
final TypeRules rules;
4960
final CodeChecker _checker;
50-
final SourceGraph _graph;
5161
final SourceNode _entryNode;
5262
List<LibraryInfo> _libraries = <LibraryInfo>[];
53-
final List<CodeGenerator> _generators;
54-
final bool _hashing;
63+
final _generators = <CodeGenerator>[];
64+
bool _hashing;
5565
bool _failure = false;
5666

5767
factory Compiler(CompilerOptions options,
@@ -61,15 +71,15 @@ class Compiler {
6171
? new TypeResolver.fromMock(mockSdkSources, options)
6272
: new TypeResolver.fromDir(options.dartSdkPath, options);
6373
}
74+
var context = resolver.context;
6475

6576
if (reporter == null) {
6677
reporter = options.dumpInfo
67-
? new SummaryReporter(options.logLevel)
68-
: new LogReporter(options.useColors);
78+
? new SummaryReporter(context, options.logLevel)
79+
: new LogReporter(context, useColors: options.useColors);
6980
}
70-
var graph = new SourceGraph(resolver.context, reporter, options);
71-
var rules =
72-
new RestrictedRules(resolver.context.typeProvider, options: options);
81+
var graph = new SourceGraph(context, reporter, options);
82+
var rules = new RestrictedRules(context.typeProvider, options: options);
7383
var checker = new CodeChecker(rules, reporter, options);
7484

7585
var inputFile = options.entryPointFile;
@@ -80,26 +90,26 @@ class Compiler {
8090
? ResolverOptions.implicitHtmlFile
8191
: inputFile));
8292
var entryNode = graph.nodeFromUri(inputUri);
83-
var outputDir = options.outputDir;
84-
var generators = <CodeGenerator>[];
93+
94+
return new Compiler._(
95+
options, context, reporter, rules, checker, entryNode);
96+
}
97+
98+
Compiler._(this.options, this.context, this._reporter, this.rules,
99+
this._checker, this._entryNode) {
85100
if (options.dumpSrcDir != null) {
86-
generators.add(new EmptyDartGenerator(
87-
options.dumpSrcDir, entryNode.uri, rules, options));
101+
_generators.add(new EmptyDartGenerator(this));
88102
}
89-
if (outputDir != null) {
90-
generators.add(options.outputDart
91-
? new DartGenerator(outputDir, entryNode.uri, rules, options)
92-
: new JSGenerator(outputDir, entryNode.uri, rules, options));
103+
if (options.outputDir != null) {
104+
_generators.add(
105+
options.outputDart ? new DartGenerator(this) : new JSGenerator(this));
93106
}
94-
return new Compiler._(options, resolver, reporter, rules, checker, graph,
95-
entryNode, generators,
96-
// TODO(sigmund): refactor to support hashing of the dart output?
97-
options.enableHashing && generators.length == 1 && !options.outputDart);
107+
// TODO(sigmund): refactor to support hashing of the dart output?
108+
_hashing =
109+
options.enableHashing && _generators.length == 1 && !options.outputDart;
98110
}
99111

100-
Compiler._(this._options, this._resolver, this._reporter, this._rules,
101-
this._checker, this._graph, this._entryNode, this._generators,
102-
this._hashing);
112+
Uri get entryPointUri => _entryNode.uri;
103113

104114
bool _buildSource(SourceNode node) {
105115
if (node is HtmlSourceNode) {
@@ -118,30 +128,30 @@ class Compiler {
118128
}
119129

120130
void _buildHtmlFile(HtmlSourceNode node) {
121-
if (_options.outputDir == null) return;
131+
if (options.outputDir == null) return;
122132
var uri = node.source.uri;
123133
_reporter.enterHtml(uri);
124-
var output = generateEntryHtml(node, _options);
134+
var output = generateEntryHtml(node, options);
125135
if (output == null) {
126136
_failure = true;
127137
return;
128138
}
129139
_reporter.leaveHtml();
130140
var filename = path.basename(node.uri.path);
131-
String outputFile = path.join(_options.outputDir, filename);
141+
String outputFile = path.join(options.outputDir, filename);
132142
new File(outputFile).writeAsStringSync(output);
133143

134-
if (_options.outputDart) return;
144+
if (options.outputDart) return;
135145
}
136146

137147
void _buildResourceFile(ResourceSourceNode node) {
138148
// ResourceSourceNodes files that just need to be copied over to the output
139149
// location. These can be external dependencies or pieces of the
140150
// dev_compiler runtime.
141-
if (_options.outputDir == null || _options.outputDart) return;
151+
if (options.outputDir == null || options.outputDart) return;
142152
var filepath = resourceOutputPath(node.uri, _entryNode.uri);
143153
assert(filepath != null);
144-
filepath = path.join(_options.outputDir, filepath);
154+
filepath = path.join(options.outputDir, filepath);
145155
var dir = path.dirname(filepath);
146156
new Directory(dir).createSync(recursive: true);
147157
new File.fromUri(node.source.uri).copySync(filepath);
@@ -156,10 +166,10 @@ class Compiler {
156166
void _buildDartLibrary(DartSourceNode node) {
157167
var source = node.source;
158168
// TODO(sigmund): find out from analyzer team if there is a better way
159-
_resolver.context.applyChanges(new ChangeSet()..changedSource(source));
160-
var entryUnit = _resolver.context.resolveCompilationUnit2(source, source);
169+
context.applyChanges(new ChangeSet()..changedSource(source));
170+
var entryUnit = context.resolveCompilationUnit2(source, source);
161171
var lib = entryUnit.element.enclosingElement;
162-
if (!_options.checkSdk && lib.isInSdk) return;
172+
if (!options.checkSdk && lib.isInSdk) return;
163173
var current = node.info;
164174
if (current != null) {
165175
assert(current.library == lib);
@@ -168,25 +178,25 @@ class Compiler {
168178
}
169179
_reporter.enterLibrary(source.uri);
170180
_libraries.add(current);
171-
_rules.currentLibraryInfo = current;
181+
rules.currentLibraryInfo = current;
172182

173183
var resolvedParts = node.parts
174-
.map((p) => _resolver.context.resolveCompilationUnit2(p.source, source))
184+
.map((p) => context.resolveCompilationUnit2(p.source, source))
175185
.toList(growable: false);
176186
var libraryUnit = new LibraryUnit(entryUnit, resolvedParts);
177187
bool failureInLib = false;
178188
for (var unit in libraryUnit.libraryThenParts) {
179189
var unitSource = unit.element.source;
180-
_reporter.enterSource(unitSource);
190+
_reporter.enterCompilationUnit(unit);
181191
// TODO(sigmund): integrate analyzer errors with static-info (issue #6).
182-
failureInLib = _resolver.logErrors(unitSource, _reporter) || failureInLib;
192+
failureInLib = logErrors(unitSource) || failureInLib;
183193
_checker.visitCompilationUnit(unit);
184194
if (_checker.failure) failureInLib = true;
185-
_reporter.leaveSource();
195+
_reporter.leaveCompilationUnit();
186196
}
187197
if (failureInLib) {
188198
_failure = true;
189-
if (!_options.forceCompile) return;
199+
if (!options.forceCompile) return;
190200
}
191201

192202
for (var cg in _generators) {
@@ -196,6 +206,21 @@ class Compiler {
196206
_reporter.leaveLibrary();
197207
}
198208

209+
/// Log any errors encountered when resolving [source] and return whether any
210+
/// errors were found.
211+
bool logErrors(Source source) {
212+
List<analyzer.AnalysisError> errors = context.getErrors(source).errors;
213+
bool failure = false;
214+
if (errors.isNotEmpty) {
215+
for (var error in errors) {
216+
var message = new AnalyzerError.from(error);
217+
if (message.level == Level.SEVERE) failure = true;
218+
_reporter.log(message);
219+
}
220+
}
221+
return failure;
222+
}
223+
199224
CheckerResults run() {
200225
var clock = new Stopwatch()..start();
201226

@@ -204,13 +229,13 @@ class Compiler {
204229
// like more than one script tag (see .severe messages in
205230
// dependency_graph.dart). Such failures should be reported back
206231
// here so we can mark failure=true in the CheckerResutls.
207-
rebuild(_entryNode, _graph, _buildSource);
232+
rebuild(_entryNode, _buildSource);
208233
_dumpInfoIfRequested();
209234
clock.stop();
210235
var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
211236
_log.fine('Compiled ${_libraries.length} libraries in ${time} s\n');
212237
return new CheckerResults(
213-
_libraries, _rules, _failure || _options.forceCompile);
238+
_libraries, rules, _failure || options.forceCompile);
214239
}
215240

216241
void _runAgain() {
@@ -219,7 +244,7 @@ class Compiler {
219244
int changed = 0;
220245

221246
// TODO(sigmund): propagate failures here (see TODO in run).
222-
rebuild(_entryNode, _graph, (n) {
247+
rebuild(_entryNode, (n) {
223248
changed++;
224249
return _buildSource(n);
225250
});
@@ -230,12 +255,12 @@ class Compiler {
230255
}
231256

232257
_dumpInfoIfRequested() {
233-
if (!_options.dumpInfo || _reporter is! SummaryReporter) return;
258+
if (!options.dumpInfo || _reporter is! SummaryReporter) return;
234259
var result = (_reporter as SummaryReporter).result;
235-
if (!_options.serverMode) print(summaryToString(result));
236-
var filepath = _options.serverMode
237-
? path.join(_options.outputDir, 'messages.json')
238-
: _options.dumpInfoFile;
260+
if (!options.serverMode) print(summaryToString(result));
261+
var filepath = options.serverMode
262+
? path.join(options.outputDir, 'messages.json')
263+
: options.dumpInfoFile;
239264
if (filepath == null) return;
240265
new File(filepath).writeAsStringSync(JSON.encode(result.toJsonMap()));
241266
}
@@ -272,7 +297,7 @@ class CompilerServer {
272297
CompilerServer._(
273298
Compiler compiler, this.outDir, this.host, this.port, String entryPath)
274299
: this.compiler = compiler,
275-
this._entryPath = compiler._options.useImplicitHtml
300+
this._entryPath = compiler.options.useImplicitHtml
276301
? ResolverOptions.implicitHtmlFile
277302
: entryPath;
278303

0 commit comments

Comments
 (0)