Chromium Code Reviews| Index: sdk/lib/_internal/compiler/implementation/compiler.dart |
| diff --git a/sdk/lib/_internal/compiler/implementation/compiler.dart b/sdk/lib/_internal/compiler/implementation/compiler.dart |
| index 1aa47b763707039c52ea28dda80d0bcf9c61a5b0..43226a03cd84e28fbee4b53a372ab7723cf2f27d 100644 |
| --- a/sdk/lib/_internal/compiler/implementation/compiler.dart |
| +++ b/sdk/lib/_internal/compiler/implementation/compiler.dart |
| @@ -81,6 +81,17 @@ class PostProcessTask { |
| PostProcessTask(this.element, this.action); |
| } |
| +// TODO(rnystrom): Now that file reading is asynchronous, this task is no longer |
| +// used and the time spent reading files isn't easily measurable (or even that |
| +// well-defined). As Peter says: |
| +// |
| +// As far as I can tell, we can only count how much time is being spent reading |
| +// files by having a global counter that covers the time from the beginning of |
| +// the compiler until it reaches a point when all libraries are scanned, and |
| +// subtract the time spent in other tasks. |
| +// |
| +// I think you should drop measureAsync. I think we can measure the time spent |
| +// reading files in dart2js.dart (you can just add a TODO). |
| class ReadingFilesTask extends CompilerTask { |
| ReadingFilesTask(Compiler compiler) : super(compiler); |
| String get name => 'Reading input files'; |
| @@ -400,6 +411,15 @@ abstract class Compiler implements DiagnosticListener { |
| Element createInvocationMirrorElement; |
| Element get currentElement => _currentElement; |
| + |
| + void _setCurrentElement(Element newElement, Element oldElement) { |
| + assert(invariant( |
| + newElement == null ? CURRENT_ELEMENT_SPANNABLE : newElement, |
| + _currentElement == oldElement, |
| + message: "Current element mismatch. " |
| + "Expected $oldElement but found $_currentElement.")); |
| + _currentElement = newElement; |
| + } |
| /** |
| * Perform an operation, [f], returning the return value from [f]. If an |
| @@ -408,16 +428,15 @@ abstract class Compiler implements DiagnosticListener { |
| */ |
| withCurrentElement(Element element, f()) { |
| Element old = currentElement; |
| - _currentElement = element; |
| + _setCurrentElement(element, old); |
| try { |
| - return f(); |
| + var result = f(); |
| + assert(invariant(element, result is! Future, |
| + message: '"withCurrentElement" called with asynchronous function. ' |
| + 'Use "withCurrentElementAsync" instead.')); |
| + return result; |
| } on SpannableAssertionFailure catch (ex) { |
| - if (!hasCrashed) { |
| - SourceSpan span = spanFromSpannable(ex.node); |
| - reportError(ex.node, MessageKind.GENERIC, {'text': ex.message}); |
| - pleaseReportCrash(); |
| - } |
| - hasCrashed = true; |
| + onSpannableAssertionFailure(ex); |
| rethrow; |
| } on CompilerCancelledException catch (ex) { |
| rethrow; |
| @@ -426,14 +445,51 @@ abstract class Compiler implements DiagnosticListener { |
| // do not have enough stack space. |
| rethrow; |
| } catch (ex) { |
| - try { |
| - unhandledExceptionOnElement(element); |
| - } catch (doubleFault) { |
| - // Ignoring exceptions in exception handling. |
| - } |
| + onUnhandledException(element); |
| rethrow; |
| } finally { |
| - _currentElement = old; |
| + _setCurrentElement(old, element); |
| + } |
| + } |
| + |
| + Future withCurrentElementAsync(Element element, Future f()) { |
| + Element old = currentElement; |
| + _setCurrentElement(element, old); |
| + try { |
| + return f().catchError((ex) { |
|
ahe
2013/08/02 11:50:19
I don't think this will work. I'l talk to you in p
Johnni Winther
2013/08/02 13:47:47
Removed.
|
| + if (ex is SpannableAssertionFailure) { |
| + onSpannableAssertionFailure(ex); |
| + } else if (ex is CompilerCancelledException || |
| + ex is StackOverflowError) { |
| + // Do nothing. |
|
ahe
2013/08/02 11:50:19
Why is that?
|
| + } else { |
| + onUnhandledException(element); |
| + } |
| + return new Future.error(ex); |
| + }) |
| + .whenComplete(() { |
| + _setCurrentElement(old, element); |
| + }); |
| + } catch (ex) { |
| + _setCurrentElement(old, element); |
| + return new Future.error(ex); |
| + } |
| + } |
| + |
| + void onSpannableAssertionFailure(SpannableAssertionFailure ex) { |
| + if (!hasCrashed) { |
| + SourceSpan span = spanFromSpannable(ex.node); |
| + reportError(ex.node, MessageKind.GENERIC, {'text': ex.message}); |
| + pleaseReportCrash(); |
| + } |
| + hasCrashed = true; |
| + } |
| + |
| + void onUnhandledException(Element element) { |
| + try { |
| + unhandledExceptionOnElement(element); |
| + } catch (doubleFault) { |
| + // Ignoring exceptions in exception handling. |
| } |
| } |
| @@ -668,14 +724,17 @@ abstract class Compiler implements DiagnosticListener { |
| reportDiagnostic(null, message, api.Diagnostic.VERBOSE_INFO); |
| } |
| - bool run(Uri uri) { |
| + Future<bool> run(Uri uri) { |
| totalCompileTime.start(); |
| - try { |
| - runCompiler(uri); |
| - } on CompilerCancelledException catch (exception) { |
| - log('Error: $exception'); |
| - return false; |
| - } catch (exception) { |
| + |
| + return runCompiler(uri).then((_) { |
| + return true; |
| + }).catchError((error) { |
| + if (error is CompilerCancelledException) { |
| + log('Error: $error'); |
| + return false; |
| + } |
| + |
| try { |
| if (!hasCrashed) { |
| hasCrashed = true; |
| @@ -687,12 +746,13 @@ abstract class Compiler implements DiagnosticListener { |
| } catch (doubleFault) { |
| // Ignoring exceptions in exception handling. |
| } |
| - rethrow; |
| - } finally { |
| + throw error; |
| + }).whenComplete(() { |
| tracer.close(); |
| totalCompileTime.stop(); |
| - } |
| - return !compilationFailed; |
| + }).then((_) { |
| + return !compilationFailed; |
| + }); |
| } |
| bool hasIsolateSupport() => isolateLibrary != null; |
| @@ -749,7 +809,7 @@ abstract class Compiler implements DiagnosticListener { |
| } |
| } |
| - LibraryElement scanBuiltinLibrary(String filename); |
| + Future<LibraryElement> scanBuiltinLibrary(String filename); |
| void initializeSpecialClasses() { |
| final List missingCoreClasses = []; |
| @@ -825,29 +885,38 @@ abstract class Compiler implements DiagnosticListener { |
| listClass.lookupConstructor(callConstructor); |
| } |
| - void scanBuiltinLibraries() { |
| - jsHelperLibrary = scanBuiltinLibrary('_js_helper'); |
| - interceptorsLibrary = scanBuiltinLibrary('_interceptors'); |
| - foreignLibrary = scanBuiltinLibrary('_foreign_helper'); |
| - isolateHelperLibrary = scanBuiltinLibrary('_isolate_helper'); |
| - |
| - assertMethod = jsHelperLibrary.find(const SourceString('assertHelper')); |
| - identicalFunction = coreLibrary.find(const SourceString('identical')); |
| - |
| - initializeSpecialClasses(); |
| - |
| - functionClass.ensureResolved(this); |
| - functionApplyMethod = |
| - functionClass.lookupLocalMember(const SourceString('apply')); |
| - jsInvocationMirrorClass.ensureResolved(this); |
| - invokeOnMethod = jsInvocationMirrorClass.lookupLocalMember(INVOKE_ON); |
| - |
| - if (preserveComments) { |
| - var uri = new Uri(scheme: 'dart', path: 'mirrors'); |
| - LibraryElement libraryElement = |
| - libraryLoader.loadLibrary(uri, null, uri); |
| - documentClass = libraryElement.find(const SourceString('Comment')); |
| - } |
| + Future scanBuiltinLibraries() { |
| + return scanBuiltinLibrary('_js_helper').then((LibraryElement library) { |
| + jsHelperLibrary = library; |
| + return scanBuiltinLibrary('_interceptors'); |
| + }).then((LibraryElement library) { |
| + interceptorsLibrary = library; |
| + return scanBuiltinLibrary('_foreign_helper'); |
| + }).then((LibraryElement library) { |
| + foreignLibrary = library; |
| + return scanBuiltinLibrary('_isolate_helper'); |
| + }).then((LibraryElement library) { |
| + isolateHelperLibrary = library; |
| + |
| + assertMethod = jsHelperLibrary.find(const SourceString('assertHelper')); |
| + identicalFunction = coreLibrary.find(const SourceString('identical')); |
| + |
| + initializeSpecialClasses(); |
| + |
| + functionClass.ensureResolved(this); |
| + functionApplyMethod = |
| + functionClass.lookupLocalMember(const SourceString('apply')); |
| + jsInvocationMirrorClass.ensureResolved(this); |
| + invokeOnMethod = jsInvocationMirrorClass.lookupLocalMember(INVOKE_ON); |
| + |
| + if (preserveComments) { |
| + var uri = new Uri(scheme: 'dart', path: 'mirrors'); |
| + return libraryLoader.loadLibrary(uri, null, uri).then( |
| + (LibraryElement libraryElement) { |
| + documentClass = libraryElement.find(const SourceString('Comment')); |
| + }); |
| + } |
| + }); |
| } |
| void importHelperLibrary(LibraryElement library) { |
| @@ -862,131 +931,138 @@ abstract class Compiler implements DiagnosticListener { |
| */ |
| Uri resolvePatchUri(String dartLibraryPath); |
| - void runCompiler(Uri uri) { |
| + Future runCompiler(Uri uri) { |
| // TODO(ahe): This prevents memory leaks when invoking the compiler |
| // multiple times. Implement a better mechanism where StringWrapper |
| // instances are shared on a per library basis. |
| SourceString.canonicalizedValues.clear(); |
| assert(uri != null || analyzeOnly); |
| - scanBuiltinLibraries(); |
| - if (librariesToAnalyzeWhenRun != null) { |
| - for (Uri libraryUri in librariesToAnalyzeWhenRun) { |
| - log('analyzing $libraryUri ($buildId)'); |
| - libraryLoader.loadLibrary(libraryUri, null, libraryUri); |
| - } |
| - } |
| - if (uri != null) { |
| - if (analyzeOnly) { |
| - log('analyzing $uri ($buildId)'); |
| - } else { |
| - log('compiling $uri ($buildId)'); |
| + return scanBuiltinLibraries().then((_) { |
| + if (librariesToAnalyzeWhenRun != null) { |
| + return Future.forEach(librariesToAnalyzeWhenRun, (libraryUri) { |
| + log('analyzing $libraryUri ($buildId)'); |
| + return libraryLoader.loadLibrary(libraryUri, null, libraryUri); |
| + }); |
| } |
| - mainApp = libraryLoader.loadLibrary(uri, null, uri); |
| - } |
| - Element main = null; |
| - if (mainApp != null) { |
| - main = mainApp.find(MAIN); |
| - if (main == null) { |
| - if (!analyzeOnly) { |
| - // Allow analyze only of libraries with no main. |
| - reportFatalError( |
| - mainApp, |
| - MessageKind.GENERIC, |
| - {'text': 'Error: Could not find "${MAIN.slowToString()}".'}); |
| - } else if (!analyzeAll) { |
| - reportFatalError( |
| - mainApp, |
| - MessageKind.GENERIC, |
| - {'text': 'Error: Could not find "${MAIN.slowToString()}". ' |
| - 'No source will be analyzed. ' |
| - 'Use "--analyze-all" to analyze all code in the library.'}); |
| - } |
| - } else { |
| - if (!main.isFunction()) { |
| - reportFatalError( |
| - main, |
| - MessageKind.GENERIC, |
| - {'text': 'Error: "${MAIN.slowToString()}" is not a function.'}); |
| + }).then((_) { |
| + if (uri != null) { |
| + if (analyzeOnly) { |
| + log('analyzing $uri ($buildId)'); |
| + } else { |
| + log('compiling $uri ($buildId)'); |
| } |
| - FunctionElement mainMethod = main; |
| - FunctionSignature parameters = mainMethod.computeSignature(this); |
| - parameters.forEachParameter((Element parameter) { |
| - reportError( |
| - parameter, |
| - MessageKind.GENERIC, |
| - {'text': |
| - 'Error: "${MAIN.slowToString()}" cannot have parameters.'}); |
| + return libraryLoader.loadLibrary(uri, null, uri) |
| + .then((LibraryElement library) { |
| + mainApp = library; |
| }); |
| } |
| + }).then((_) { |
| + Element main = null; |
| + if (mainApp != null) { |
| + main = mainApp.find(MAIN); |
| + if (main == null) { |
| + if (!analyzeOnly) { |
| + // Allow analyze only of libraries with no main. |
| + reportFatalError( |
| + mainApp, |
| + MessageKind.GENERIC, |
| + {'text': 'Error: Could not find "${MAIN.slowToString()}".'}); |
| + } else if (!analyzeAll) { |
| + reportFatalError( |
| + mainApp, |
| + MessageKind.GENERIC, |
| + {'text': 'Error: Could not find "${MAIN.slowToString()}". ' |
| + 'No source will be analyzed. ' |
| + 'Use "--analyze-all" to analyze all code in the library.'}); |
| + } |
| + } else { |
| + if (!main.isFunction()) { |
| + reportFatalError( |
| + main, |
| + MessageKind.GENERIC, |
| + {'text': 'Error: "${MAIN.slowToString()}" is not a function.'}); |
| + } |
| + FunctionElement mainMethod = main; |
| + FunctionSignature parameters = mainMethod.computeSignature(this); |
| + parameters.forEachParameter((Element parameter) { |
| + reportError( |
| + parameter, |
| + MessageKind.GENERIC, |
| + {'text': |
| + 'Error: "${MAIN.slowToString()}" cannot have parameters.'}); |
| + }); |
| + } |
| - mirrorUsageAnalyzerTask.analyzeUsage(mainApp); |
| - |
| - // In order to see if a library is deferred, we must compute the |
| - // compile-time constants that are metadata. This means adding |
| - // something to the resolution queue. So we cannot wait with |
| - // this until after the resolution queue is processed. |
| - // TODO(ahe): Clean this up, for example, by not enqueueing |
| - // classes only used for metadata. |
| - deferredLoadTask.findDeferredLibraries(mainApp); |
| - } |
| + mirrorUsageAnalyzerTask.analyzeUsage(mainApp); |
| - log('Resolving...'); |
| - phase = PHASE_RESOLVING; |
| - if (analyzeAll) { |
| - libraries.forEach( |
| - (_, lib) => fullyEnqueueLibrary(lib, enqueuer.resolution)); |
| - } |
| - // Elements required by enqueueHelpers are global dependencies |
| - // that are not pulled in by a particular element. |
| - backend.enqueueHelpers(enqueuer.resolution, globalDependencies); |
| - processQueue(enqueuer.resolution, main); |
| - enqueuer.resolution.logSummary(log); |
| + // In order to see if a library is deferred, we must compute the |
| + // compile-time constants that are metadata. This means adding |
| + // something to the resolution queue. So we cannot wait with |
| + // this until after the resolution queue is processed. |
| + // TODO(ahe): Clean this up, for example, by not enqueueing |
| + // classes only used for metadata. |
| + deferredLoadTask.findDeferredLibraries(mainApp); |
| + } |
| - if (compilationFailed) return; |
| - if (analyzeOnly) return; |
| - assert(main != null); |
| - phase = PHASE_DONE_RESOLVING; |
| - |
| - // TODO(ahe): Remove this line. Eventually, enqueuer.resolution |
| - // should know this. |
| - world.populate(); |
| - // Compute whole-program-knowledge that the backend needs. (This might |
| - // require the information computed in [world.populate].) |
| - backend.onResolutionComplete(); |
| - |
| - deferredLoadTask.onResolutionComplete(main); |
| - |
| - log('Inferring types...'); |
| - typesTask.onResolutionComplete(main); |
| - |
| - log('Compiling...'); |
| - phase = PHASE_COMPILING; |
| - // TODO(johnniwinther): Move these to [CodegenEnqueuer]. |
| - if (hasIsolateSupport()) { |
| - enqueuer.codegen.addToWorkList( |
| - isolateHelperLibrary.find(Compiler.START_ROOT_ISOLATE)); |
| - enqueuer.codegen.registerGetOfStaticFunction(mainApp.find(MAIN)); |
| - } |
| - if (enabledNoSuchMethod) { |
| - enqueuer.codegen.registerInvocation(noSuchMethodSelector); |
| - enqueuer.codegen.addToWorkList(createInvocationMirrorElement); |
| - } |
| - if (compileAll) { |
| - libraries.forEach((_, lib) => fullyEnqueueLibrary(lib, enqueuer.codegen)); |
| - } |
| - processQueue(enqueuer.codegen, main); |
| - enqueuer.codegen.logSummary(log); |
| + log('Resolving...'); |
| + phase = PHASE_RESOLVING; |
| + if (analyzeAll) { |
| + libraries.forEach( |
| + (_, lib) => fullyEnqueueLibrary(lib, enqueuer.resolution)); |
| + } |
| + // Elements required by enqueueHelpers are global dependencies |
| + // that are not pulled in by a particular element. |
| + backend.enqueueHelpers(enqueuer.resolution, globalDependencies); |
| + processQueue(enqueuer.resolution, main); |
| + enqueuer.resolution.logSummary(log); |
| + |
| + if (compilationFailed) return; |
| + if (analyzeOnly) return; |
| + assert(main != null); |
| + phase = PHASE_DONE_RESOLVING; |
| + |
| + // TODO(ahe): Remove this line. Eventually, enqueuer.resolution |
| + // should know this. |
| + world.populate(); |
| + // Compute whole-program-knowledge that the backend needs. (This might |
| + // require the information computed in [world.populate].) |
| + backend.onResolutionComplete(); |
| + |
| + deferredLoadTask.onResolutionComplete(main); |
| + |
| + log('Inferring types...'); |
| + typesTask.onResolutionComplete(main); |
| + |
| + log('Compiling...'); |
| + phase = PHASE_COMPILING; |
| + // TODO(johnniwinther): Move these to [CodegenEnqueuer]. |
| + if (hasIsolateSupport()) { |
| + enqueuer.codegen.addToWorkList( |
| + isolateHelperLibrary.find(Compiler.START_ROOT_ISOLATE)); |
| + enqueuer.codegen.registerGetOfStaticFunction(mainApp.find(MAIN)); |
| + } |
| + if (enabledNoSuchMethod) { |
| + enqueuer.codegen.registerInvocation(noSuchMethodSelector); |
| + enqueuer.codegen.addToWorkList(createInvocationMirrorElement); |
| + } |
| + if (compileAll) { |
| + libraries.forEach((_, lib) => fullyEnqueueLibrary(lib, |
| + enqueuer.codegen)); |
| + } |
| + processQueue(enqueuer.codegen, main); |
| + enqueuer.codegen.logSummary(log); |
| - if (compilationFailed) return; |
| + if (compilationFailed) return; |
| - backend.assembleProgram(); |
| + backend.assembleProgram(); |
| - checkQueues(); |
| + checkQueues(); |
| - if (compilationFailed) { |
| - assembledCode = null; // Signals failure. |
| - } |
| + if (compilationFailed) { |
| + assembledCode = null; // Signals failure. |
| + } |
| + }); |
| } |
| void fullyEnqueueLibrary(LibraryElement library, Enqueuer world) { |
| @@ -1318,7 +1394,7 @@ abstract class Compiler implements DiagnosticListener { |
| * |
| * See [LibraryLoader] for terminology on URIs. |
| */ |
| - Script readScript(Uri readableUri, [Node node]) { |
| + Future<Script> readScript(Uri readableUri, [Node node]) { |
| unimplemented('Compiler.readScript'); |
| } |
| @@ -1462,10 +1538,14 @@ class SourceSpan { |
| bool invariant(Spannable spannable, var condition, {var message: null}) { |
| // TODO(johnniwinther): Use [spannable] and [message] to provide better |
| // information on assertion errors. |
| + if (spannable == null) { |
| + throw new SpannableAssertionFailure(CURRENT_ELEMENT_SPANNABLE, |
| + "Spannable was null for invariant. Use CURRENT_ELEMENT_SPANNABLE."); |
| + } |
| if (condition is Function){ |
| condition = condition(); |
| } |
| - if (spannable == null || !condition) { |
| + if (!condition) { |
| if (message is Function) { |
| message = message(); |
| } |