| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // 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. |
| |
| part of dart2js; |
| |
| class EnqueueTask extends CompilerTask { |
| final Enqueuer codegen; |
| final Enqueuer resolution; |
| |
| String get name => 'Enqueue'; |
| |
| EnqueueTask(Compiler compiler) |
| : codegen = new Enqueuer('codegen enqueuer', compiler, |
| compiler.backend.createItemCompilationContext), |
| resolution = new Enqueuer('resolution enqueuer', compiler, |
| compiler.backend.createItemCompilationContext), |
| super(compiler) { |
| codegen.task = this; |
| resolution.task = this; |
| } |
| } |
| |
| class Enqueuer { |
| final String name; |
| final Compiler compiler; // TODO(ahe): Remove this dependency. |
| final Function itemCompilationContextCreator; |
| final Map<String, Link<Element>> instanceMembersByName; |
| final Set<ClassElement> seenClasses; |
| final Universe universe; |
| final Queue<WorkItem> queue; |
| |
| /** |
| * Map from declaration elements to the [TreeElements] object holding the |
| * resolution mapping for the element implementation. |
| * |
| * Invariant: Key elements are declaration elements. |
| */ |
| final Map<Element, TreeElements> resolvedElements; |
| |
| bool queueIsClosed = false; |
| EnqueueTask task; |
| |
| Enqueuer(this.name, this.compiler, |
| ItemCompilationContext itemCompilationContextCreator()) |
| : this.itemCompilationContextCreator = itemCompilationContextCreator, |
| instanceMembersByName = new Map<String, Link<Element>>(), |
| seenClasses = new Set<ClassElement>(), |
| universe = new Universe(), |
| queue = new Queue<WorkItem>(), |
| resolvedElements = new Map<Element, TreeElements>(); |
| |
| bool get isResolutionQueue => identical(compiler.enqueuer.resolution, this); |
| |
| TreeElements getCachedElements(Element element) { |
| // TODO(ngeoffray): Get rid of this check. |
| if (element.enclosingElement.isClosure()) { |
| closureMapping.ClosureClassElement cls = element.enclosingElement; |
| element = cls.methodElement; |
| } |
| Element owner = element.getOutermostEnclosingMemberOrTopLevel(); |
| return compiler.enqueuer.resolution.resolvedElements[owner.declaration]; |
| } |
| |
| /** |
| * Documentation wanted -- johnniwinther |
| * |
| * Invariant: [element] must be a declaration element. |
| */ |
| String lookupCode(Element element) { |
| assert(invariant(element, element.isDeclaration)); |
| return universe.generatedCode[element].toString(); |
| } |
| |
| /** |
| * Documentation wanted -- johnniwinther |
| * |
| * Invariant: [element] must be a declaration element. |
| */ |
| void addToWorkList(Element element, [TreeElements elements]) { |
| assert(invariant(element, element.isDeclaration)); |
| if (element.isForeign()) return; |
| if (queueIsClosed) { |
| if (isResolutionQueue && getCachedElements(element) != null) return; |
| compiler.internalErrorOnElement(element, "Work list is closed."); |
| } |
| if (!isResolutionQueue && |
| identical(element.kind, ElementKind.GENERATIVE_CONSTRUCTOR)) { |
| registerInstantiatedClass(element.getEnclosingClass()); |
| } |
| if (elements == null) { |
| elements = getCachedElements(element); |
| } |
| if (isResolutionQueue) { |
| compiler.world.registerUsedElement(element); |
| } |
| |
| queue.add(new WorkItem(element, elements, itemCompilationContextCreator())); |
| |
| // Enable runtime type support if we discover a getter called runtimeType. |
| // We have to enable runtime type before hitting the codegen, so |
| // that constructors know whether they need to generate code for |
| // runtime type. |
| if (element.isGetter() && element.name == Compiler.RUNTIME_TYPE) { |
| compiler.enabledRuntimeType = true; |
| } else if (element == compiler.functionApplyMethod) { |
| compiler.enabledFunctionApply = true; |
| } else if (element == compiler.invokeOnMethod) { |
| compiler.enabledInvokeOn = true; |
| } |
| |
| // Enable isolate support if we start using something from the |
| // isolate library. |
| LibraryElement library = element.getLibrary(); |
| if (!compiler.hasIsolateSupport() |
| && library.uri.toString() == 'dart:isolate') { |
| compiler.enableIsolateSupport(library); |
| } |
| } |
| |
| /** |
| * Documentation wanted -- johnniwinther |
| * |
| * Invariant: [element] must be a declaration element. |
| */ |
| void eagerRecompile(Element element) { |
| assert(invariant(element, element.isDeclaration)); |
| universe.generatedCode.remove(element); |
| universe.generatedBailoutCode.remove(element); |
| addToWorkList(element); |
| } |
| |
| void registerInstantiatedClass(ClassElement cls) { |
| if (cls.isInterface()) { |
| compiler.internalErrorOnElement( |
| // Use the current element, as this is where cls is referenced from. |
| compiler.currentElement, |
| 'Expected a class, but $cls is an interface.'); |
| } |
| universe.instantiatedClasses.add(cls); |
| onRegisterInstantiatedClass(cls); |
| } |
| |
| bool checkNoEnqueuedInvokedInstanceMethods() { |
| task.measure(() { |
| // Run through the classes and see if we need to compile methods. |
| for (ClassElement classElement in universe.instantiatedClasses) { |
| for (ClassElement currentClass = classElement; |
| currentClass != null; |
| currentClass = currentClass.superclass) { |
| processInstantiatedClass(currentClass); |
| } |
| } |
| }); |
| return true; |
| } |
| |
| void processInstantiatedClass(ClassElement cls) { |
| cls.implementation.forEachMember(processInstantiatedClassMember); |
| } |
| |
| /** |
| * Documentation wanted -- johnniwinther |
| */ |
| void processInstantiatedClassMember(ClassElement cls, Element member) { |
| assert(invariant(member, member.isDeclaration)); |
| if (universe.generatedCode.containsKey(member)) return; |
| if (resolvedElements[member] != null) return; |
| if (!member.isInstanceMember()) return; |
| if (member.isField()) return; |
| |
| String memberName = member.name.slowToString(); |
| Link<Element> members = instanceMembersByName.putIfAbsent( |
| memberName, () => const Link<Element>()); |
| instanceMembersByName[memberName] = members.prepend(member); |
| |
| if (member.kind == ElementKind.FUNCTION) { |
| if (member.name == Compiler.NO_SUCH_METHOD) { |
| compiler.enableNoSuchMethod(member); |
| } |
| if (universe.hasInvocation(member, compiler)) { |
| return addToWorkList(member); |
| } |
| // If there is a property access with the same name as a method we |
| // need to emit the method. |
| if (universe.hasInvokedGetter(member, compiler)) { |
| // We will emit a closure, so make sure the closure class is |
| // generated. |
| compiler.closureClass.ensureResolved(compiler); |
| registerInstantiatedClass(compiler.closureClass); |
| return addToWorkList(member); |
| } |
| } else if (member.kind == ElementKind.GETTER) { |
| if (universe.hasInvokedGetter(member, compiler)) { |
| return addToWorkList(member); |
| } |
| // We don't know what selectors the returned closure accepts. If |
| // the set contains any selector we have to assume that it matches. |
| if (universe.hasInvocation(member, compiler)) { |
| return addToWorkList(member); |
| } |
| } else if (identical(member.kind, ElementKind.SETTER)) { |
| if (universe.hasInvokedSetter(member, compiler)) { |
| return addToWorkList(member); |
| } |
| } |
| } |
| |
| void onRegisterInstantiatedClass(ClassElement cls) { |
| task.measure(() { |
| // The class must be resolved to compute the set of all |
| // supertypes. |
| cls.ensureResolved(compiler); |
| |
| for (Link<DartType> supertypes = cls.allSupertypesAndSelf; |
| !supertypes.isEmpty; supertypes = supertypes.tail) { |
| cls = supertypes.head.element; |
| if (seenClasses.contains(cls)) continue; |
| seenClasses.add(cls); |
| cls.ensureResolved(compiler); |
| if (!cls.isInterface()) { |
| cls.implementation.forEachMember(processInstantiatedClassMember); |
| } |
| if (isResolutionQueue) { |
| compiler.resolver.checkMembers(cls); |
| } |
| |
| if (compiler.enableTypeAssertions) { |
| // We need to register is checks and helpers for checking |
| // assignments to fields. |
| // TODO(ngeoffray): This should really move to the backend. |
| cls.localMembers.forEach((Element member) { |
| if (!member.isInstanceMember() || !member.isField()) return; |
| DartType type = member.computeType(compiler); |
| registerIsCheck(type); |
| SourceString helper = compiler.backend.getCheckedModeHelper(type); |
| if (helper != null) { |
| Element helperElement = compiler.findHelper(helper); |
| registerStaticUse(helperElement); |
| } |
| }); |
| } |
| } |
| }); |
| } |
| |
| void registerNewSelector(SourceString name, |
| Selector selector, |
| Map<SourceString, Set<Selector>> selectorsMap) { |
| if (name != selector.name) { |
| String message = "$name != ${selector.name} (${selector.kind})"; |
| compiler.internalError("Wrong selector name: $message."); |
| } |
| Set<Selector> selectors = |
| selectorsMap.putIfAbsent(name, () => new Set<Selector>()); |
| if (!selectors.contains(selector)) { |
| selectors.add(selector); |
| handleUnseenSelector(name, selector); |
| } |
| } |
| |
| void registerInvocation(SourceString methodName, Selector selector) { |
| task.measure(() { |
| registerNewSelector(methodName, selector, universe.invokedNames); |
| }); |
| } |
| |
| void registerInvokedGetter(SourceString getterName, Selector selector) { |
| task.measure(() { |
| registerNewSelector(getterName, selector, universe.invokedGetters); |
| }); |
| } |
| |
| void registerInvokedSetter(SourceString setterName, Selector selector) { |
| task.measure(() { |
| registerNewSelector(setterName, selector, universe.invokedSetters); |
| }); |
| } |
| |
| processInstanceMembers(SourceString n, bool f(Element e)) { |
| String memberName = n.slowToString(); |
| Link<Element> members = instanceMembersByName[memberName]; |
| if (members != null) { |
| LinkBuilder<Element> remaining = new LinkBuilder<Element>(); |
| for (; !members.isEmpty; members = members.tail) { |
| if (!f(members.head)) remaining.addLast(members.head); |
| } |
| instanceMembersByName[memberName] = remaining.toLink(); |
| } |
| } |
| |
| void handleUnseenSelector(SourceString methodName, Selector selector) { |
| processInstanceMembers(methodName, (Element member) { |
| if (selector.applies(member, compiler)) { |
| addToWorkList(member); |
| return true; |
| } |
| return false; |
| }); |
| } |
| |
| /** |
| * Documentation wanted -- johnniwinther |
| * |
| * Invariant: [element] must be a declaration element. |
| */ |
| void registerStaticUse(Element element) { |
| if (element == null) return; |
| assert(invariant(element, element.isDeclaration)); |
| addToWorkList(element); |
| } |
| |
| void registerGetOfStaticFunction(FunctionElement element) { |
| registerStaticUse(element); |
| universe.staticFunctionsNeedingGetter.add(element); |
| } |
| |
| void registerDynamicInvocation(SourceString methodName, Selector selector) { |
| assert(selector != null); |
| registerInvocation(methodName, selector); |
| } |
| |
| void registerDynamicInvocationOf(Element element) { |
| addToWorkList(element); |
| } |
| |
| void registerDynamicGetter(SourceString methodName, Selector selector) { |
| registerInvokedGetter(methodName, selector); |
| } |
| |
| void registerDynamicSetter(SourceString methodName, Selector selector) { |
| registerInvokedSetter(methodName, selector); |
| } |
| |
| void registerFieldGetter(SourceString getterName, |
| LibraryElement library, |
| DartType type) { |
| task.measure(() { |
| Selector getter = new Selector.getter(getterName, library); |
| registerNewSelector(getterName, |
| new TypedSelector(type, getter), |
| universe.fieldGetters); |
| }); |
| } |
| |
| void registerFieldSetter(SourceString setterName, |
| LibraryElement library, |
| DartType type) { |
| task.measure(() { |
| Selector setter = new Selector.setter(setterName, library); |
| registerNewSelector(setterName, |
| new TypedSelector(type, setter), |
| universe.fieldSetters); |
| }); |
| } |
| |
| void registerIsCheck(DartType type) { |
| universe.isChecks.add(type); |
| } |
| |
| void forEach(f(WorkItem work)) { |
| while (!queue.isEmpty) { |
| f(queue.removeLast()); // TODO(kasperl): Why isn't this removeFirst? |
| } |
| } |
| |
| String toString() => 'Enqueuer($name)'; |
| |
| registerUsedSelector(Selector selector) { |
| Element interceptor = compiler.backend.getInterceptor(selector); |
| if (interceptor != null) { |
| registerStaticUse(interceptor); |
| } |
| } |
| } |