OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library fletchc_incremental; | |
6 | |
7 import 'dart:async' show | |
8 EventSink, | |
9 Future; | |
10 | |
11 import 'dart:developer' show | |
12 UserTag; | |
13 | |
14 import 'package:compiler/src/apiimpl.dart' show | |
15 CompilerImpl; | |
16 | |
17 import 'package:compiler/compiler_new.dart' show | |
18 CompilerDiagnostics, | |
19 CompilerInput, | |
20 CompilerOutput, | |
21 Diagnostic; | |
22 | |
23 import 'package:compiler/src/elements/elements.dart' show | |
24 ClassElement, | |
25 ConstructorElement, | |
26 Element, | |
27 FunctionElement, | |
28 LibraryElement; | |
29 | |
30 import 'package:compiler/src/library_loader.dart' show | |
31 ReuseLibrariesFunction; | |
32 | |
33 import 'fletch_reuser.dart' show | |
34 IncrementalCompilerContext, | |
35 FletchReuser, | |
36 Logger; | |
37 | |
38 import '../fletch_compiler.dart' show | |
39 FletchCompiler; | |
40 | |
41 import '../src/debug_info.dart' show | |
42 DebugInfo; | |
43 | |
44 import '../src/class_debug_info.dart' show | |
45 ClassDebugInfo; | |
46 | |
47 import '../src/fletch_selector.dart' show | |
48 FletchSelector; | |
49 | |
50 import '../src/fletch_compiler_implementation.dart' show | |
51 FletchCompilerImplementation, | |
52 OutputProvider; | |
53 | |
54 import '../fletch_system.dart'; | |
55 | |
56 import '../src/fletch_backend.dart' show | |
57 FletchBackend; | |
58 | |
59 import '../src/hub/exit_codes.dart' as exit_codes; | |
60 | |
61 import 'package:compiler/src/source_file_provider.dart' show | |
62 SourceFileProvider; | |
63 | |
64 import 'package:compiler/src/tokens/token.dart' show | |
65 Token; | |
66 | |
67 import 'package:compiler/src/diagnostics/source_span.dart' show | |
68 SourceSpan; | |
69 | |
70 part 'caching_compiler.dart'; | |
71 | |
72 const List<String> INCREMENTAL_OPTIONS = const <String>[ | |
73 '--disable-type-inference', | |
74 '--incremental-support', | |
75 '--generate-code-with-compile-time-errors', | |
76 '--no-source-maps', // TODO(ahe): Remove this. | |
77 ]; | |
78 | |
79 enum IncrementalMode { | |
80 /// Incremental compilation is turned off | |
81 none, | |
82 | |
83 /// Incremental compilation is turned on for a limited set of features that | |
84 /// are known to be fully implemented. Initially, this limited set of | |
85 /// features will be instance methods without signature changes. As other | |
86 /// features mature, they will be enabled in this mode. | |
87 production, | |
88 | |
89 /// All incremental features are turned on even if we know that we don't | |
90 /// always generate correct code. Initially, this covers features such as | |
91 /// schema changes. | |
92 experimental, | |
93 } | |
94 | |
95 class IncrementalCompiler { | |
96 final Uri libraryRoot; | |
97 final Uri nativesJson; | |
98 final Uri packageConfig; | |
99 final Uri fletchVm; | |
100 final CompilerInput inputProvider; | |
101 final List<String> options; | |
102 final CompilerOutput outputProvider; | |
103 final Map<String, dynamic> environment; | |
104 final IncrementalCompilerContext _context; | |
105 final IncrementalMode support; | |
106 final String platform; | |
107 final Map<Uri, Uri> _updatedFiles = new Map<Uri, Uri>(); | |
108 | |
109 FletchCompilerImplementation _compiler; | |
110 | |
111 IncrementalCompiler( | |
112 {this.libraryRoot, | |
113 this.nativesJson, | |
114 this.packageConfig, | |
115 this.fletchVm, | |
116 this.inputProvider, | |
117 CompilerDiagnostics diagnosticHandler, | |
118 this.options, | |
119 this.outputProvider, | |
120 this.environment, | |
121 this.support: IncrementalMode.none, | |
122 this.platform}) | |
123 : _context = new IncrementalCompilerContext(diagnosticHandler) { | |
124 // if (libraryRoot == null) { | |
125 // throw new ArgumentError('libraryRoot is null.'); | |
126 // } | |
127 if (inputProvider == null) { | |
128 throw new ArgumentError('inputProvider is null.'); | |
129 } | |
130 if (outputProvider == null) { | |
131 throw new ArgumentError('outputProvider is null.'); | |
132 } | |
133 if (diagnosticHandler == null) { | |
134 throw new ArgumentError('diagnosticHandler is null.'); | |
135 } | |
136 if (platform == null) { | |
137 throw new ArgumentError('platform is null.'); | |
138 } | |
139 _context.incrementalCompiler = this; | |
140 } | |
141 | |
142 bool get isProductionModeEnabled { | |
143 return support == IncrementalMode.production || | |
144 support == IncrementalMode.experimental; | |
145 } | |
146 | |
147 bool get isExperimentalModeEnabled { | |
148 return support == IncrementalMode.experimental; | |
149 } | |
150 | |
151 LibraryElement get mainApp => _compiler.mainApp; | |
152 | |
153 FletchCompilerImplementation get compiler => _compiler; | |
154 | |
155 /// Perform a full compile of [script]. This will reset the incremental | |
156 /// compiler. | |
157 /// | |
158 /// Error messages will be reported relative to [base]. | |
159 /// | |
160 /// Notice: a full compile means not incremental. The part of the program | |
161 /// that is compiled is determined by tree shaking. | |
162 Future<bool> compile(Uri script, Uri base) { | |
163 _compiler = null; | |
164 _updatedFiles.clear(); | |
165 return _reuseCompiler(null, base: base).then((CompilerImpl compiler) { | |
166 _compiler = compiler; | |
167 return compiler.run(script); | |
168 }); | |
169 } | |
170 | |
171 /// Perform a full analysis of [script]. This will reset the incremental | |
172 /// compiler. | |
173 /// | |
174 /// Error messages will be reported relative to [base]. | |
175 /// | |
176 /// Notice: a full analysis is analogous to a full compile, that is, full | |
177 /// analysis not incremental. The part of the program that is analyzed is | |
178 /// determined by tree shaking. | |
179 Future<int> analyze(Uri script, Uri base) { | |
180 _compiler = null; | |
181 int initialErrorCount = _context.errorCount; | |
182 int initialProblemCount = _context.problemCount; | |
183 return _reuseCompiler(null, analyzeOnly: true, base: base).then( | |
184 (CompilerImpl compiler) { | |
185 // Don't try to reuse the compiler object. | |
186 return compiler.run(script).then((_) { | |
187 return _context.problemCount == initialProblemCount | |
188 ? 0 | |
189 : _context.errorCount == initialErrorCount | |
190 ? exit_codes.ANALYSIS_HAD_NON_ERROR_PROBLEMS | |
191 : exit_codes.ANALYSIS_HAD_ERRORS; | |
192 }); | |
193 }); | |
194 } | |
195 | |
196 Future<CompilerImpl> _reuseCompiler( | |
197 ReuseLibrariesFunction reuseLibraries, | |
198 {bool analyzeOnly: false, | |
199 Uri base}) { | |
200 List<String> options = this.options == null | |
201 ? <String> [] : new List<String>.from(this.options); | |
202 options.addAll(INCREMENTAL_OPTIONS); | |
203 if (analyzeOnly) { | |
204 options.add("--analyze-only"); | |
205 } | |
206 return reuseCompiler( | |
207 cachedCompiler: _compiler, | |
208 libraryRoot: libraryRoot, | |
209 packageConfig: packageConfig, | |
210 nativesJson: nativesJson, | |
211 fletchVm: fletchVm, | |
212 inputProvider: inputProvider, | |
213 diagnosticHandler: _context, | |
214 options: options, | |
215 outputProvider: outputProvider, | |
216 environment: environment, | |
217 reuseLibraries: reuseLibraries, | |
218 platform: platform, | |
219 base: base, | |
220 incrementalCompiler: this); | |
221 } | |
222 | |
223 void _checkCompilationFailed() { | |
224 if (!isExperimentalModeEnabled && _compiler.compilationFailed) { | |
225 throw new IncrementalCompilationFailed( | |
226 "Unable to reuse compiler due to compile-time errors"); | |
227 } | |
228 } | |
229 | |
230 /// Perform an incremental compilation of [updatedFiles]. [compile] must have | |
231 /// been called once before calling this method. | |
232 /// | |
233 /// Error messages will be reported relative to [base], if [base] is not | |
234 /// provided the previous set [base] will be used. | |
235 Future<FletchDelta> compileUpdates( | |
236 FletchSystem currentSystem, | |
237 Map<Uri, Uri> updatedFiles, | |
238 {Logger logTime, | |
239 Logger logVerbose, | |
240 Uri base}) { | |
241 _checkCompilationFailed(); | |
242 if (logTime == null) { | |
243 logTime = (_) {}; | |
244 } | |
245 if (logVerbose == null) { | |
246 logVerbose = (_) {}; | |
247 } | |
248 updatedFiles.forEach((Uri from, Uri to) { | |
249 _updatedFiles[from] = to; | |
250 }); | |
251 Future mappingInputProvider(Uri uri) { | |
252 Uri updatedFile = _updatedFiles[uri]; | |
253 return inputProvider.readFromUri(updatedFile == null ? uri : updatedFile); | |
254 } | |
255 FletchReuser reuser = new FletchReuser( | |
256 _compiler, | |
257 mappingInputProvider, | |
258 logTime, | |
259 logVerbose, | |
260 _context); | |
261 _context.registerUriWithUpdates(updatedFiles.keys); | |
262 return _reuseCompiler(reuser.reuseLibraries, base: base).then( | |
263 (CompilerImpl compiler) async { | |
264 _compiler = compiler; | |
265 FletchDelta delta = await reuser.computeUpdateFletch(currentSystem); | |
266 _checkCompilationFailed(); | |
267 return delta; | |
268 }); | |
269 } | |
270 | |
271 FletchDelta computeInitialDelta() { | |
272 FletchBackend backend = _compiler.backend; | |
273 return backend.computeDelta(); | |
274 } | |
275 | |
276 String lookupFunctionName(FletchFunction function) { | |
277 if (function.isParameterStub) return "<parameter stub>"; | |
278 Element element = function.element; | |
279 if (element == null) return function.name; | |
280 if (element.isConstructor) { | |
281 ConstructorElement constructor = element; | |
282 ClassElement enclosing = constructor.enclosingClass; | |
283 String name = (constructor.name == null || constructor.name.length == 0) | |
284 ? '' | |
285 : '.${constructor.name}'; | |
286 String postfix = function.isInitializerList ? ' initializer' : ''; | |
287 return '${enclosing.name}$name$postfix'; | |
288 } | |
289 | |
290 ClassElement enclosing = element.enclosingClass; | |
291 if (enclosing == null) return function.name; | |
292 return '${enclosing.name}.${function.name}'; | |
293 } | |
294 | |
295 ClassDebugInfo createClassDebugInfo(FletchClass klass) { | |
296 return _compiler.context.backend.createClassDebugInfo(klass); | |
297 } | |
298 | |
299 String lookupFunctionNameBySelector(int selector) { | |
300 int id = FletchSelector.decodeId(selector); | |
301 return _compiler.context.symbols[id]; | |
302 } | |
303 | |
304 DebugInfo createDebugInfo( | |
305 FletchFunction function, | |
306 FletchSystem currentSystem) { | |
307 return _compiler.context.backend.createDebugInfo(function, currentSystem); | |
308 } | |
309 | |
310 DebugInfo debugInfoForPosition( | |
311 Uri file, | |
312 int position, | |
313 FletchSystem currentSystem) { | |
314 return _compiler.debugInfoForPosition(file, position, currentSystem); | |
315 } | |
316 | |
317 int positionInFileFromPattern(Uri file, int line, String pattern) { | |
318 return _compiler.positionInFileFromPattern(file, line, pattern); | |
319 } | |
320 | |
321 int positionInFile(Uri file, int line, int column) { | |
322 return _compiler.positionInFile(file, line, column); | |
323 } | |
324 | |
325 Iterable<Uri> findSourceFiles(Pattern pattern) { | |
326 return _compiler.findSourceFiles(pattern); | |
327 } | |
328 | |
329 SourceSpan createSourceSpan( | |
330 Token begin, | |
331 Token end, | |
332 Uri uri, | |
333 Element element) { | |
334 Uri update = _updatedFiles[uri]; | |
335 if (update != null) { | |
336 // TODO(ahe): Compute updated position. | |
337 return new SourceSpan(update, 0, 0); | |
338 } | |
339 return new SourceSpan.fromTokens(uri, begin, end); | |
340 } | |
341 } | |
342 | |
343 class IncrementalCompilationFailed { | |
344 final String reason; | |
345 | |
346 const IncrementalCompilationFailed(this.reason); | |
347 | |
348 String toString() => "Can't incrementally compile program.\n\n$reason"; | |
349 } | |
350 | |
351 String unparseIncrementalMode(IncrementalMode mode) { | |
352 switch (mode) { | |
353 case IncrementalMode.none: | |
354 return "none"; | |
355 | |
356 case IncrementalMode.production: | |
357 return "production"; | |
358 | |
359 case IncrementalMode.experimental: | |
360 return "experimental"; | |
361 } | |
362 throw "Unhandled $mode"; | |
363 } | |
364 | |
365 IncrementalMode parseIncrementalMode(String text) { | |
366 switch (text) { | |
367 case "none": | |
368 return IncrementalMode.none; | |
369 | |
370 case "production": | |
371 return IncrementalMode.production; | |
372 | |
373 case "experimental": | |
374 return IncrementalMode.experimental; | |
375 | |
376 } | |
377 return null; | |
378 } | |
OLD | NEW |