// 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. class EnqueueTask extends CompilerTask { final Map> instanceMembersByName; final Set seenClasses; String get name() => 'Enqueue'; EnqueueTask(Compiler compiler) : instanceMembersByName = new Map>(), seenClasses = new Set(), super(compiler); bool checkNoEnqueuedInvokedInstanceMethods() { measure(() { // Run through the classes and see if we need to compile methods. for (ClassElement classElement in compiler.universe.instantiatedClasses) { for (ClassElement currentClass = classElement; currentClass !== null; currentClass = currentClass.superclass) { processInstantiatedClass(currentClass); } } }); return true; } void processInstantiatedClass(ClassElement cls) { cls.members.forEach(processInstantiatedClassMember); } void registerFieldClosureInvocations() { measure(() { // Make sure that the closure understands a call with the given // selector. For a method-invocation of the form o.foo(a: 499), we // need to make sure that closures can handle the optional argument if // there exists a field or getter 'foo'. var names = compiler.universe.instantiatedClassInstanceFields; // TODO(ahe): Might be enough to use invokedGetters. for (SourceString name in names) { Set invokedSelectors = compiler.universe.invokedNames[name]; if (invokedSelectors != null) { for (Selector selector in invokedSelectors) { compiler.registerDynamicInvocation(Namer.CLOSURE_INVOCATION_NAME, selector); } } } }); } void processInstantiatedClassMember(Element member) { if (compiler.universe.generatedCode.containsKey(member)) return; if (!member.isInstanceMember()) return; String memberName = member.name.slowToString(); Link members = instanceMembersByName.putIfAbsent( memberName, () => const EmptyLink()); instanceMembersByName[memberName] = members.prepend(member); if (member.kind === ElementKind.GETTER || member.kind === ElementKind.FIELD) { compiler.universe.instantiatedClassInstanceFields.add(member.name); } if (member.kind == ElementKind.FUNCTION) { if (member.name == Compiler.NO_SUCH_METHOD) { compiler.enableNoSuchMethod(member); } Set selectors = compiler.universe.invokedNames[member.name]; if (selectors != null) { FunctionElement functionMember = member; FunctionParameters parameters = functionMember.computeParameters(compiler); for (Selector selector in selectors) { if (selector.applies(parameters)) { return compiler.addToWorkList(member); } } } // If there is a property access with the same name as a method we // need to emit the method. if (compiler.universe.invokedGetters.contains(member.name)) { // We will emit a closure, so make sure the closure class is // generated. compiler.closureClass.ensureResolved(compiler); compiler.registerInstantiatedClass(compiler.closureClass); return compiler.addToWorkList(member); } } else if (member.kind == ElementKind.GETTER) { if (compiler.universe.invokedGetters.contains(member.name)) { return compiler.addToWorkList(member); } // A method invocation like in o.foo(x, y) might actually be an // invocation of the getter foo followed by an invocation of the // returned closure. Set invokedSelectors = compiler.universe.invokedNames[member.name]; // We don't know what selectors the returned closure accepts. If // the set contains any selector we have to assume that it matches. if (invokedSelectors !== null && !invokedSelectors.isEmpty()) { return compiler.addToWorkList(member); } } else if (member.kind === ElementKind.SETTER) { if (compiler.universe.invokedSetters.contains(member.name)) { return compiler.addToWorkList(member); } } } void onRegisterInstantiatedClass(ClassElement cls) { measure(() { while (cls !== null) { if (seenClasses.contains(cls)) return; seenClasses.add(cls); // TODO(ahe): Don't call resolveType, instead, call this method // when resolveType is called. compiler.resolveClass(cls); cls.members.forEach(processInstantiatedClassMember); cls = cls.superclass; } }); } void registerInvocation(SourceString methodName, Selector selector) { measure(() { Map> invokedNames = compiler.universe.invokedNames; Set selectors = invokedNames.putIfAbsent(methodName, () => new Set()); if (!selectors.contains(selector)) { selectors.add(selector); handleUnseenInvocation(methodName, selector); } }); } void registerGetter(SourceString methodName) { measure(() { if (!compiler.universe.invokedGetters.contains(methodName)) { compiler.universe.invokedGetters.add(methodName); handleUnseenGetter(methodName); } }); } void registerSetter(SourceString methodName) { measure(() { if (!compiler.universe.invokedSetters.contains(methodName)) { compiler.universe.invokedSetters.add(methodName); handleUnseenSetter(methodName); } }); } processInstanceMembers(SourceString n, bool f(Element e)) { String memberName = n.slowToString(); Link members = instanceMembersByName[memberName]; if (members !== null) { LinkBuilder remaining = new LinkBuilder(); for (; !members.isEmpty(); members = members.tail) { if (!f(members.head)) remaining.addLast(members.head); } instanceMembersByName[memberName] = remaining.toLink(); } } void handleUnseenInvocation(SourceString methodName, Selector selector) { processInstanceMembers(methodName, (Element member) { if (member.isGetter()) { compiler.addToWorkList(member); return true; } else if (member.isFunction()) { FunctionElement functionMember = member; FunctionParameters parameters = functionMember.computeParameters(compiler); if (selector.applies(parameters)) { compiler.addToWorkList(member); return true; } } return false; }); } void handleUnseenGetter(SourceString methodName) { processInstanceMembers(methodName, (Element member) { if (member.isGetter() || member.isFunction()) { compiler.addToWorkList(member); return true; } else { return false; } }); } void handleUnseenSetter(SourceString methodName) { processInstanceMembers(methodName, (Element member) { if (member.isSetter()) { compiler.addToWorkList(member); return true; } else { return false; } }); } }