| 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 |