Chromium Code Reviews| Index: dart/lib/compiler/implementation/compiler.dart |
| diff --git a/dart/lib/compiler/implementation/compiler.dart b/dart/lib/compiler/implementation/compiler.dart |
| index 2b4679c73003466009e44e4fbf641bcc0d964b2b..b3f16992d4ebd9665157b472f113375c047ec38c 100644 |
| --- a/dart/lib/compiler/implementation/compiler.dart |
| +++ b/dart/lib/compiler/implementation/compiler.dart |
| @@ -2,6 +2,13 @@ |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| + |
| +/** |
| + * If true, print a warning for each method that was resolved, but not |
| + * compiled. |
| + */ |
| +final bool REPORT_EXCESS_RESOLUTION = false; |
| + |
| class WorkItem { |
| final Element element; |
| TreeElements resolutionTree; |
| @@ -15,8 +22,8 @@ class WorkItem { |
| String run(Compiler compiler, Enqueuer world) { |
| String code = world.universe.generatedCode[element]; |
| if (code !== null) return code; |
| - if (!isAnalyzed()) compiler.analyze(this); |
| - return compiler.codegen(this); |
| + resolutionTree = compiler.analyze(this, world); |
| + return compiler.codegen(this, world); |
| } |
| } |
| @@ -25,6 +32,13 @@ class Backend { |
| Backend(this.compiler); |
| + void enqueueAllTopLevelFunctions(LibraryElement lib, Enqueuer world) { |
| + lib.forEachExport((Element e) { |
| + if (e.isFunction()) world.addToWorkList(e); |
| + }); |
| + } |
| + |
| + abstract void enqueueHelpers(Enqueuer world); |
| abstract String codegen(WorkItem work); |
| abstract void processNativeClasses(world, libraries); |
| abstract void assembleProgram(); |
| @@ -49,6 +63,17 @@ class JavaScriptBackend extends Backend { |
| generator = new SsaCodeGeneratorTask(this); |
| } |
| + void enqueueHelpers(Enqueuer world) { |
| + enqueueAllTopLevelFunctions(compiler.jsHelperLibrary, world); |
| + enqueueAllTopLevelFunctions(compiler.interceptorsLibrary, world); |
| + for (var helper in [const SourceString('Closure'), |
| + const SourceString('ConstantMap'), |
| + const SourceString('ConstantProtoMap')]) { |
| + var e = compiler.findHelper(helper); |
| + if (e !== null) world.registerInstantiatedClass(e); |
| + } |
| + } |
| + |
| String codegen(WorkItem work) { |
| HGraph graph = builder.build(work); |
| optimizer.optimize(work, graph); |
| @@ -205,6 +230,8 @@ class Compiler implements DiagnosticListener { |
| void cancel([String reason, Node node, Token token, |
| HInstruction instruction, Element element]) { |
| + assembledCode = null; // Compilation failed. Make sure that we |
| + // don't return a bogus result. |
| SourceSpan span = const SourceSpan(null, null, null); |
| if (node !== null) { |
| span = spanFromNode(node); |
| @@ -248,15 +275,25 @@ class Compiler implements DiagnosticListener { |
| void enableNoSuchMethod(Element element) { |
| // TODO(ahe): Move this method to Enqueuer. |
| if (enabledNoSuchMethod) return; |
| - if (element.enclosingElement == objectClass) return; |
| + if (element.enclosingElement === objectClass) { |
| + enqueuer.resolution.registerDynamicInvocationOf(element); |
| + return; |
| + } |
| enabledNoSuchMethod = true; |
| + enqueuer.resolution.registerInvocation(NO_SUCH_METHOD, |
| + Selector.INVOCATION_2); |
| enqueuer.codegen.registerInvocation(NO_SUCH_METHOD, |
| - new Selector.invocation(2)); |
| + Selector.INVOCATION_2); |
| } |
| void enableIsolateSupport(LibraryElement element) { |
| // TODO(ahe): Move this method to Enqueuer. |
| isolateLibrary = element; |
| + enqueuer.resolution.addToWorkList(element.find(START_ROOT_ISOLATE)); |
| + enqueuer.resolution.addToWorkList( |
| + element.find(const SourceString('_currentIsolate'))); |
| + enqueuer.resolution.addToWorkList( |
| + element.find(const SourceString('_callInIsolate'))); |
| enqueuer.codegen.addToWorkList(element.find(START_ROOT_ISOLATE)); |
| } |
| @@ -366,50 +403,118 @@ class Compiler implements DiagnosticListener { |
| // should know this. |
| world.populate(this, libraries.getValues()); |
| - // Not yet ready to process the enqueuer.resolution queue... |
| - // processQueue(enqueuer.resolution); |
| + log('Resolving...'); |
| + backend.enqueueHelpers(enqueuer.resolution); |
| + processQueue(enqueuer.resolution, main); |
| + log('Resolved ${enqueuer.resolution.resolvedElements.length} elements.'); |
| + |
| + log('Compiling...'); |
| processQueue(enqueuer.codegen, main); |
| + log('Compiled ${codegenWorld.generatedCode.length} methods.'); |
| backend.assembleProgram(); |
| - if (!enqueuer.codegen.queue.isEmpty()) { |
| - internalErrorOnElement(enqueuer.codegen.queue.first().element, |
| - "work list is not empty"); |
| - } |
| + |
| + checkQueues(); |
| } |
| processQueue(Enqueuer world, Element main) { |
| backend.processNativeClasses(world, libraries.getValues()); |
| world.addToWorkList(main); |
| codegenProgress.reset(); |
| - while (!world.queue.isEmpty()) { |
| - WorkItem work = world.queue.removeLast(); |
| + world.forEach((WorkItem work) { |
| withCurrentElement(work.element, () => work.run(this, world)); |
| - } |
| + }); |
| world.queueIsClosed = true; |
| assert(world.checkNoEnqueuedInvokedInstanceMethods()); |
| world.registerFieldClosureInvocations(); |
| } |
| + /** |
| + * Perform various checks of the queues. This includes checking that |
| + * the queues are empty (nothing was added after we stopped |
| + * processing the quese). Also compute the number of methods that |
|
ngeoffray
2012/06/12 12:44:14
quese -> queues
ahe
2012/06/12 18:47:13
Done.
|
| + * were resolved, but not compiled (aka excess resolution). |
| + */ |
| + checkQueues() { |
| + for (var world in [enqueuer.resolution, enqueuer.codegen]) { |
| + world.forEach((WorkItem work) { |
| + internalErrorOnElement(work.element, "Work list is not empty."); |
| + }); |
| + } |
| + var resolved = new Set.from(enqueuer.resolution.resolvedElements.getKeys()); |
| + for (Element e in codegenWorld.generatedCode.getKeys()) { |
| + resolved.remove(e); |
| + } |
| + for (Element e in new Set.from(resolved)) { |
| + if (e.isClass() || |
| + e.isField() || |
| + e.isTypeVariable() || |
| + e.isTypedef() || |
| + e.kind === ElementKind.ABSTRACT_FIELD) { |
| + resolved.remove(e); |
| + } |
| + if (e.kind === ElementKind.GENERATIVE_CONSTRUCTOR) { |
| + if (e.enclosingElement.isInterface()) { |
| + resolved.remove(e); |
| + } |
| + resolved.remove(e); |
| + |
| + } |
| + if (e.getLibrary() === jsHelperLibrary) { |
| + resolved.remove(e); |
| + } |
| + if (e.getLibrary() === interceptorsLibrary) { |
| + resolved.remove(e); |
| + } |
| + } |
| + log('Excess resolution work: ${resolved.length}.'); |
| + if (!REPORT_EXCESS_RESOLUTION) return; |
| + for (Element e in resolved) { |
| + SourceSpan span = spanFromElement(e); |
| + reportDiagnostic(span, 'Warning: $e resolved but not compiled.', false); |
| + } |
| + } |
| + |
| TreeElements analyzeElement(Element element) { |
| + TreeElements elements = enqueuer.resolution.getCachedElements(element); |
| + if (elements !== null) return elements; |
| + if (element is AbstractFieldElement) { |
|
ngeoffray
2012/06/12 12:44:14
Maybe add this check together with the allowed che
ahe
2012/06/12 18:47:13
Done.
|
| + return null; |
| + } |
| + final int allowed = ElementCategory.VARIABLE | ElementCategory.FUNCTION |
| + | ElementCategory.FACTORY; |
| + if (!element.isAccessor() && (element.kind.category & allowed) == 0) { |
| + return null; |
| + } |
| assert(parser !== null); |
| Node tree = parser.parse(element); |
| validator.validate(tree); |
| unparseValidator.check(element); |
| - TreeElements elements = resolver.resolve(element); |
| + elements = resolver.resolve(element); |
| checker.check(tree, elements); |
| return elements; |
| } |
| - TreeElements analyze(WorkItem work) { |
| - work.resolutionTree = analyzeElement(work.element); |
| - return work.resolutionTree; |
| + TreeElements analyze(WorkItem work, Enqueuer world) { |
| + if (work.isAnalyzed()) return work.resolutionTree; |
| + Element element = work.element; |
| + TreeElements result = world.getCachedElements(element); |
| + if (result !== null) return result; |
| + if (world !== enqueuer.resolution) { |
| + internalErrorOnElement(element, |
| + 'Internal error: unresolved element: $element.'); |
| + } |
| + result = analyzeElement(element); |
| + enqueuer.resolution.resolvedElements[element] = result; |
| + return result; |
| } |
| - String codegen(WorkItem work) { |
| + String codegen(WorkItem work, Enqueuer world) { |
| + if (world !== enqueuer.codegen) return null; |
|
ngeoffray
2012/06/12 12:44:14
Can we actually prevent this situation? Or is that
ahe
2012/06/12 18:47:13
I'm not sure I understand your question.
ngeoffray
2012/06/13 07:31:57
It does not feel right that the resolver queue end
ahe
2012/06/13 08:20:41
I have some ideas for cleaning this up. If enqueue
|
| if (codegenProgress.elapsedInMs() > 500) { |
| // TODO(ahe): Add structured diagnostics to the compiler API and |
| // use it to separate this from the --verbose option. |
| - log('compiled ${codegenWorld.generatedCode.length} methods'); |
| + log('Compiled ${codegenWorld.generatedCode.length} methods.'); |
| codegenProgress.reset(); |
| } |
| if (work.element.kind.category == ElementCategory.VARIABLE) { |
| @@ -479,14 +584,16 @@ class Compiler implements DiagnosticListener { |
| abstract void reportDiagnostic(SourceSpan span, String message, bool fatal); |
| - SourceSpan spanFromTokens(Token begin, Token end) { |
| + SourceSpan spanFromTokens(Token begin, Token end, [Uri uri]) { |
| if (begin === null || end === null) { |
| // TODO(ahe): We can almost always do better. Often it is only |
| // end that is null. Otherwise, we probably know the current |
| // URI. |
| - throw 'cannot find tokens to produce error message'; |
| + throw 'Cannot find tokens to produce error message.'; |
| + } |
| + if (uri === null) { |
| + uri = currentElement.getCompilationUnit().script.uri; |
| } |
| - Uri uri = currentElement.getCompilationUnit().script.uri; |
| return SourceSpan.withOffsets(begin, end, (beginOffset, endOffset) => |
| new SourceSpan(uri, beginOffset, endOffset)); |
| } |
| @@ -509,11 +616,10 @@ class Compiler implements DiagnosticListener { |
| element = currentElement; |
| } |
| Token position = element.position(); |
| - if (position === null) { |
| - Uri uri = element.getCompilationUnit().script.uri; |
| - return new SourceSpan(uri, 0, 0); |
| - } |
| - return spanFromTokens(position, position); |
| + Uri uri = element.getCompilationUnit().script.uri; |
| + return (position === null) |
| + ? new SourceSpan(uri, 0, 0) |
| + : spanFromTokens(position, position, uri); |
| } |
| Script readScript(Uri uri, [ScriptTag node]) { |