| OLD | NEW |
| 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 /// Command line tool to run the checker on a Dart program. | 5 /// Command line tool to run the checker on a Dart program. |
| 6 library dev_compiler.src.compiler; | 6 library dev_compiler.src.compiler; |
| 7 | 7 |
| 8 import 'dart:async'; | 8 import 'dart:async'; |
| 9 import 'dart:collection'; | 9 import 'dart:collection'; |
| 10 import 'dart:math' as math; | 10 import 'dart:math' as math; |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 64 } | 64 } |
| 65 | 65 |
| 66 bool compile(CompilerOptions options) { | 66 bool compile(CompilerOptions options) { |
| 67 assert(!options.serverMode); | 67 assert(!options.serverMode); |
| 68 var context = createAnalysisContextWithSources( | 68 var context = createAnalysisContextWithSources( |
| 69 options.strongOptions, options.sourceOptions); | 69 options.strongOptions, options.sourceOptions); |
| 70 var reporter = createErrorReporter(context, options); | 70 var reporter = createErrorReporter(context, options); |
| 71 return new BatchCompiler(context, options, reporter: reporter).run(); | 71 return new BatchCompiler(context, options, reporter: reporter).run(); |
| 72 } | 72 } |
| 73 | 73 |
| 74 // Callback on each individual compiled library |
| 75 typedef void CompilationNotifier(String path); |
| 76 |
| 74 class BatchCompiler extends AbstractCompiler { | 77 class BatchCompiler extends AbstractCompiler { |
| 75 JSGenerator _jsGen; | 78 JSGenerator _jsGen; |
| 76 LibraryElement _dartCore; | 79 LibraryElement _dartCore; |
| 77 String _runtimeOutputDir; | 80 String _runtimeOutputDir; |
| 78 | 81 |
| 79 /// Already compiled sources, so we don't compile them again. | 82 /// Already compiled sources, so we don't check or compile them again. |
| 80 final _compiled = new HashSet<LibraryElement>(); | 83 final _compilationRecord = <LibraryElement, bool>{}; |
| 81 bool _sdkCopied = false; | 84 bool _sdkCopied = false; |
| 82 | 85 |
| 83 bool _failure = false; | 86 bool _failure = false; |
| 84 bool get failure => _failure; | 87 bool get failure => _failure; |
| 85 | 88 |
| 89 final _pendingLibraries = <LibraryUnit>[]; |
| 90 |
| 86 BatchCompiler(AnalysisContext context, CompilerOptions options, | 91 BatchCompiler(AnalysisContext context, CompilerOptions options, |
| 87 {AnalysisErrorListener reporter}) | 92 {AnalysisErrorListener reporter}) |
| 88 : super( | 93 : super( |
| 89 context, | 94 context, |
| 90 options, | 95 options, |
| 91 new ErrorCollector( | 96 new ErrorCollector( |
| 92 reporter ?? AnalysisErrorListener.NULL_LISTENER)) { | 97 reporter ?? AnalysisErrorListener.NULL_LISTENER)) { |
| 93 _inputBaseDir = options.inputBaseDir; | 98 _inputBaseDir = options.inputBaseDir; |
| 94 if (outputDir != null) { | 99 if (outputDir != null) { |
| 95 _jsGen = new JSGenerator(this); | 100 _jsGen = new JSGenerator(this); |
| 96 _runtimeOutputDir = path.join(outputDir, 'dev_compiler', 'runtime'); | 101 _runtimeOutputDir = path.join(outputDir, 'dev_compiler', 'runtime'); |
| 97 } | 102 } |
| 98 _dartCore = context.typeProvider.objectType.element.library; | 103 _dartCore = context.typeProvider.objectType.element.library; |
| 99 } | 104 } |
| 100 | 105 |
| 101 ErrorCollector get reporter => checker.reporter; | 106 ErrorCollector get reporter => checker.reporter; |
| 102 | 107 |
| 103 void reset() { | |
| 104 _compiled.clear(); | |
| 105 _sdkCopied = false; | |
| 106 } | |
| 107 | |
| 108 /// Compiles every file in [options.inputs]. | 108 /// Compiles every file in [options.inputs]. |
| 109 /// Returns true on successful compile. | 109 /// Returns true on successful compile. |
| 110 bool run() { | 110 bool run() { |
| 111 var clock = new Stopwatch()..start(); | 111 var clock = new Stopwatch()..start(); |
| 112 options.inputs.forEach(compileFromUriString); | 112 options.inputs.forEach(compileFromUriString); |
| 113 clock.stop(); | 113 clock.stop(); |
| 114 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); | 114 var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); |
| 115 _log.fine('Compiled ${_compiled.length} libraries in ${time} s\n'); | 115 _log.fine('Compiled ${_compilationRecord.length} libraries in ${time} s\n'); |
| 116 | 116 |
| 117 return !_failure; | 117 return !_failure; |
| 118 } | 118 } |
| 119 | 119 |
| 120 void compileFromUriString(String uriString) { | 120 void compileFromUriString(String uriString, [CompilationNotifier notifier]) { |
| 121 _compileFromUri(stringToUri(uriString)); | 121 _compileFromUri(stringToUri(uriString), notifier); |
| 122 } | 122 } |
| 123 | 123 |
| 124 void _compileFromUri(Uri uri) { | 124 void _compileFromUri(Uri uri, CompilationNotifier notifier) { |
| 125 _failure = false; |
| 125 if (!uri.isAbsolute) { | 126 if (!uri.isAbsolute) { |
| 126 throw new ArgumentError.value('$uri', 'uri', 'must be absolute'); | 127 throw new ArgumentError.value('$uri', 'uri', 'must be absolute'); |
| 127 } | 128 } |
| 128 var source = context.sourceFactory.forUri(Uri.encodeFull('$uri')); | 129 var source = context.sourceFactory.forUri(Uri.encodeFull('$uri')); |
| 129 if (source == null) { | 130 if (source == null) { |
| 130 throw new ArgumentError.value('$uri', 'uri', 'could not find source for'); | 131 throw new ArgumentError.value('$uri', 'uri', 'could not find source for'); |
| 131 } | 132 } |
| 132 compileSource(source); | 133 _compileSource(source, notifier); |
| 133 } | 134 } |
| 134 | 135 |
| 135 void compileSource(Source source) { | 136 void _compileSource(Source source, CompilationNotifier notifier) { |
| 136 if (AnalysisEngine.isHtmlFileName(source.uri.path)) { | 137 if (AnalysisEngine.isHtmlFileName(source.uri.path)) { |
| 137 _compileHtml(source); | 138 _compileHtml(source, notifier); |
| 138 } else { | 139 } else { |
| 139 _compileLibrary(context.computeLibraryElement(source)); | 140 _compileLibrary(context.computeLibraryElement(source), notifier); |
| 140 } | 141 } |
| 142 _processPending(); |
| 141 reporter.flush(); | 143 reporter.flush(); |
| 142 } | 144 } |
| 143 | 145 |
| 144 void _compileLibrary(LibraryElement library) { | 146 void _processPending() { |
| 145 if (!_compiled.add(library)) return; | 147 // _pendingLibraries was recorded in post-order. Process from the end |
| 148 // to ensure reverse post-order. This will ensure that we handle back |
| 149 // edges from the original depth-first search correctly. |
| 150 |
| 151 while (_pendingLibraries.isNotEmpty) { |
| 152 var unit = _pendingLibraries.removeLast(); |
| 153 var library = unit.library.element.enclosingElement; |
| 154 assert(_compilationRecord[library] == true); |
| 155 |
| 156 // Process dependences one more time to propagate failure from cycles |
| 157 for (var import in library.imports) { |
| 158 if (!_compilationRecord[import.importedLibrary]) { |
| 159 _compilationRecord[library] = false; |
| 160 } |
| 161 } |
| 162 for (var export in library.exports) { |
| 163 if (!_compilationRecord[export.exportedLibrary]) { |
| 164 _compilationRecord[library] = false; |
| 165 } |
| 166 } |
| 167 |
| 168 // Generate code if still valid |
| 169 if (_jsGen != null && |
| 170 (_compilationRecord[library] || |
| 171 options.codegenOptions.forceCompile)) { |
| 172 _jsGen.generateLibrary(unit); |
| 173 } |
| 174 } |
| 175 } |
| 176 |
| 177 bool _compileLibrary(LibraryElement library, CompilationNotifier notifier) { |
| 178 var success = _compilationRecord[library]; |
| 179 if (success != null) { |
| 180 if (!success) _failure = true; |
| 181 return success; |
| 182 } |
| 183 |
| 184 // Optimistically mark a library valid until proven otherwise |
| 185 _compilationRecord[library] = true; |
| 146 | 186 |
| 147 if (!options.checkSdk && library.source.uri.scheme == 'dart') { | 187 if (!options.checkSdk && library.source.uri.scheme == 'dart') { |
| 188 // We assume the Dart SDK is always valid |
| 148 if (_jsGen != null) _copyDartRuntime(); | 189 if (_jsGen != null) _copyDartRuntime(); |
| 149 return; | 190 return true; |
| 150 } | 191 } |
| 151 | 192 |
| 193 // Check dependences to determine if this library type checks |
| 152 // TODO(jmesserly): in incremental mode, we can skip the transitive | 194 // TODO(jmesserly): in incremental mode, we can skip the transitive |
| 153 // compile of imports/exports. | 195 // compile of imports/exports. |
| 154 _compileLibrary(_dartCore); // implicit dart:core dependency | 196 _compileLibrary(_dartCore, notifier); // implicit dart:core dependency |
| 155 for (var import in library.imports) _compileLibrary(import.importedLibrary); | 197 for (var import in library.imports) { |
| 156 for (var export in library.exports) _compileLibrary(export.exportedLibrary); | 198 if (!_compileLibrary(import.importedLibrary, notifier)) { |
| 199 _compilationRecord[library] = false; |
| 200 } |
| 201 } |
| 202 for (var export in library.exports) { |
| 203 if (!_compileLibrary(export.exportedLibrary, notifier)) { |
| 204 _compilationRecord[library] = false; |
| 205 } |
| 206 } |
| 157 | 207 |
| 208 // Check this library's own code |
| 158 var unitElements = [library.definingCompilationUnit]..addAll(library.parts); | 209 var unitElements = [library.definingCompilationUnit]..addAll(library.parts); |
| 159 var units = <CompilationUnit>[]; | 210 var units = <CompilationUnit>[]; |
| 160 | 211 |
| 161 bool failureInLib = false; | 212 bool failureInLib = false; |
| 162 for (var element in unitElements) { | 213 for (var element in unitElements) { |
| 163 var unit = context.resolveCompilationUnit(element.source, library); | 214 var unit = context.resolveCompilationUnit(element.source, library); |
| 164 units.add(unit); | 215 units.add(unit); |
| 165 failureInLib = logErrors(element.source) || failureInLib; | 216 failureInLib = logErrors(element.source) || failureInLib; |
| 217 checker.reset(); |
| 166 checker.visitCompilationUnit(unit); | 218 checker.visitCompilationUnit(unit); |
| 167 if (checker.failure) failureInLib = true; | 219 if (checker.failure) failureInLib = true; |
| 168 } | 220 } |
| 221 if (failureInLib) _compilationRecord[library] = false; |
| 169 | 222 |
| 170 if (failureInLib) { | 223 // Notifier framework if requested |
| 171 _failure = true; | 224 if (notifier != null) { |
| 172 if (!options.codegenOptions.forceCompile) return; | 225 reporter.flush(); |
| 226 notifier(getOutputPath(library.source.uri)); |
| 173 } | 227 } |
| 174 | 228 |
| 175 if (_jsGen != null) { | 229 // Record valid libraries for further dependence checking (cycles) and |
| 230 // codegen. |
| 231 |
| 232 // TODO(vsm): Restructure this to not delay code generation more than |
| 233 // necessary. We'd like to process the AST before there is any chance |
| 234 // it's cached out. We should refactor common logic in |
| 235 // server/dependency_graph and perhaps the analyzer itself. |
| 236 success = _compilationRecord[library]; |
| 237 if (success || options.codegenOptions.forceCompile) { |
| 176 var unit = units.first; | 238 var unit = units.first; |
| 177 var parts = units.skip(1).toList(); | 239 var parts = units.skip(1).toList(); |
| 178 _jsGen.generateLibrary(new LibraryUnit(unit, parts)); | 240 _pendingLibraries.add(new LibraryUnit(unit, parts)); |
| 179 } | 241 } |
| 242 |
| 243 // Return tentative success status. |
| 244 if (!success) _failure = true; |
| 245 return success; |
| 180 } | 246 } |
| 181 | 247 |
| 182 void _copyDartRuntime() { | 248 void _copyDartRuntime() { |
| 183 if (_sdkCopied) return; | 249 if (_sdkCopied) return; |
| 184 _sdkCopied = true; | 250 _sdkCopied = true; |
| 185 for (var file in defaultRuntimeFiles) { | 251 for (var file in defaultRuntimeFiles) { |
| 186 var input = new File(path.join(options.runtimeDir, file)); | 252 var input = new File(path.join(options.runtimeDir, file)); |
| 187 var output = new File(path.join(_runtimeOutputDir, file)); | 253 var output = new File(path.join(_runtimeOutputDir, file)); |
| 188 if (output.existsSync() && | 254 if (output.existsSync() && |
| 189 output.lastModifiedSync() == input.lastModifiedSync()) { | 255 output.lastModifiedSync() == input.lastModifiedSync()) { |
| 190 continue; | 256 continue; |
| 191 } | 257 } |
| 192 new Directory(path.dirname(output.path)).createSync(recursive: true); | 258 new Directory(path.dirname(output.path)).createSync(recursive: true); |
| 193 input.copySync(output.path); | 259 input.copySync(output.path); |
| 194 } | 260 } |
| 195 } | 261 } |
| 196 | 262 |
| 197 void _compileHtml(Source source) { | 263 void _compileHtml(Source source, CompilationNotifier notifier) { |
| 198 // TODO(jmesserly): reuse DartScriptsTask instead of copy/paste. | 264 // TODO(jmesserly): reuse DartScriptsTask instead of copy/paste. |
| 199 var contents = context.getContents(source); | 265 var contents = context.getContents(source); |
| 200 var document = html.parse(contents.data, generateSpans: true); | 266 var document = html.parse(contents.data, generateSpans: true); |
| 201 var scripts = document.querySelectorAll('script[type="application/dart"]'); | 267 var scripts = document.querySelectorAll('script[type="application/dart"]'); |
| 202 | 268 |
| 203 var loadedLibs = new LinkedHashSet<Uri>(); | 269 var loadedLibs = new LinkedHashSet<Uri>(); |
| 204 | 270 |
| 205 var htmlOutDir = path.dirname(getOutputPath(source.uri)); | 271 var htmlOutDir = path.dirname(getOutputPath(source.uri)); |
| 206 for (var script in scripts) { | 272 for (var script in scripts) { |
| 207 Source scriptSource = null; | 273 Source scriptSource = null; |
| 208 var srcAttr = script.attributes['src']; | 274 var srcAttr = script.attributes['src']; |
| 209 if (srcAttr == null) { | 275 if (srcAttr == null) { |
| 210 if (script.hasContent()) { | 276 if (script.hasContent()) { |
| 211 var fragments = <ScriptFragment>[]; | 277 var fragments = <ScriptFragment>[]; |
| 212 for (var node in script.nodes) { | 278 for (var node in script.nodes) { |
| 213 if (node is html.Text) { | 279 if (node is html.Text) { |
| 214 var start = node.sourceSpan.start; | 280 var start = node.sourceSpan.start; |
| 215 fragments.add(new ScriptFragment( | 281 fragments.add(new ScriptFragment( |
| 216 start.offset, start.line, start.column, node.data)); | 282 start.offset, start.line, start.column, node.data)); |
| 217 } | 283 } |
| 218 } | 284 } |
| 219 scriptSource = new DartScript(source, fragments); | 285 scriptSource = new DartScript(source, fragments); |
| 220 } | 286 } |
| 221 } else if (AnalysisEngine.isDartFileName(srcAttr)) { | 287 } else if (AnalysisEngine.isDartFileName(srcAttr)) { |
| 222 scriptSource = context.sourceFactory.resolveUri(source, srcAttr); | 288 scriptSource = context.sourceFactory.resolveUri(source, srcAttr); |
| 223 } | 289 } |
| 224 | 290 |
| 225 if (scriptSource != null) { | 291 if (scriptSource != null) { |
| 226 var lib = context.computeLibraryElement(scriptSource); | 292 var lib = context.computeLibraryElement(scriptSource); |
| 227 _compileLibrary(lib); | 293 _compileLibrary(lib, notifier); |
| 228 script.replaceWith(_linkLibraries(lib, loadedLibs, from: htmlOutDir)); | 294 script.replaceWith(_linkLibraries(lib, loadedLibs, from: htmlOutDir)); |
| 229 } | 295 } |
| 230 } | 296 } |
| 231 | 297 |
| 232 new File(getOutputPath(source.uri)).openSync(mode: FileMode.WRITE) | 298 new File(getOutputPath(source.uri)).openSync(mode: FileMode.WRITE) |
| 233 ..writeStringSync(document.outerHtml) | 299 ..writeStringSync(document.outerHtml) |
| 234 ..writeStringSync('\n') | 300 ..writeStringSync('\n') |
| 235 ..closeSync(); | 301 ..closeSync(); |
| 236 } | 302 } |
| 237 | 303 |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 453 '_rtti.js', | 519 '_rtti.js', |
| 454 '_classes.js', | 520 '_classes.js', |
| 455 '_operations.js', | 521 '_operations.js', |
| 456 'dart_runtime.js', | 522 'dart_runtime.js', |
| 457 ]; | 523 ]; |
| 458 files.addAll(corelibOrder.map((l) => l.replaceAll('.', '/') + '.js')); | 524 files.addAll(corelibOrder.map((l) => l.replaceAll('.', '/') + '.js')); |
| 459 return files; | 525 return files; |
| 460 }(); | 526 }(); |
| 461 | 527 |
| 462 final _log = new Logger('dev_compiler.src.compiler'); | 528 final _log = new Logger('dev_compiler.src.compiler'); |
| OLD | NEW |