| // 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. |
| package com.google.dart.compiler; |
| |
| import com.google.dart.compiler.ast.ASTVisitor; |
| import com.google.dart.compiler.ast.DartClass; |
| import com.google.dart.compiler.ast.DartIdentifier; |
| import com.google.dart.compiler.ast.DartMethodInvocation; |
| import com.google.dart.compiler.ast.DartParameterizedTypeNode; |
| import com.google.dart.compiler.ast.DartPropertyAccess; |
| import com.google.dart.compiler.ast.DartTypeNode; |
| import com.google.dart.compiler.ast.DartUnit; |
| import com.google.dart.compiler.resolver.Element; |
| import com.google.dart.compiler.resolver.ElementKind; |
| import com.google.dart.compiler.type.InterfaceType; |
| import com.google.dart.compiler.type.Type; |
| import com.google.dart.compiler.type.TypeKind; |
| |
| import java.net.URI; |
| |
| /** |
| * A visitor that fills in {@link LibraryDeps} for a compilation unit. |
| */ |
| public class LibraryDepsVisitor extends ASTVisitor<Void> { |
| /** |
| * Fill in {@link LibraryDeps} from a {@link DartUnit}. |
| */ |
| static void exec(DartUnit unit, LibraryDeps.Source source) { |
| LibraryDepsVisitor v = new LibraryDepsVisitor(source); |
| unit.accept(v); |
| } |
| |
| private final LibraryDeps.Source source; |
| private Element currentClass; |
| |
| private LibraryDepsVisitor(LibraryDeps.Source source) { |
| this.source = source; |
| } |
| |
| @Override |
| public Void visitIdentifier(DartIdentifier node) { |
| Element target = node.getElement(); |
| ElementKind kind = ElementKind.of(target); |
| // Add dependency on the field or method. |
| switch (kind) { |
| case FIELD: |
| case METHOD: { |
| Element enclosing = target.getEnclosingElement(); |
| addHoleIfUnqualifiedSuper(node, enclosing); |
| if (enclosing.getKind().equals(ElementKind.LIBRARY)) { |
| addElementDependency(target); |
| } |
| break; |
| } |
| } |
| // Add dependency on the computed type of identifiers. |
| switch (kind) { |
| case NONE: |
| source.addHole(node.getName()); |
| break; |
| case DYNAMIC: |
| break; |
| default: { |
| Type type = target.getType(); |
| if (type != null) { |
| Element element = type.getElement(); |
| if (ElementKind.of(element).equals(ElementKind.CLASS)) { |
| addElementDependency(element); |
| } |
| } |
| break; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Void visitPropertyAccess(DartPropertyAccess node) { |
| if (node.getQualifier() instanceof DartIdentifier) { |
| DartIdentifier qualifier = (DartIdentifier) node.getQualifier(); |
| Element target = qualifier.getElement(); |
| if (target != null && target.getKind() == ElementKind.LIBRARY) { |
| // Handle library prefixes normally. |
| // The prefix part of the qualifier doesn't contain any resolvable library source info. |
| return super.visitPropertyAccess(node); |
| } |
| } |
| // Skip rhs of property accesses, so that all identifiers we visit will be unqualified. |
| if (node.isCascade()) { |
| return null; |
| } |
| return node.getQualifier().accept(this); |
| } |
| |
| @Override |
| public Void visitClass(DartClass node) { |
| currentClass = node.getElement(); |
| node.visitChildren(this); |
| currentClass = null; |
| return null; |
| } |
| |
| @Override |
| public Void visitParameterizedTypeNode(DartParameterizedTypeNode node) { |
| if (TypeKind.of(node.getType()).equals(TypeKind.INTERFACE)) { |
| addElementDependency(((InterfaceType) node.getType()).getElement()); |
| } |
| node.visitChildren(this); |
| return null; |
| } |
| |
| @Override |
| public Void visitTypeNode(DartTypeNode node) { |
| if (TypeKind.of(node.getType()).equals(TypeKind.INTERFACE)) { |
| addElementDependency(((InterfaceType) node.getType()).getElement()); |
| } |
| node.visitChildren(this); |
| return null; |
| } |
| |
| /** |
| * Add a 'hole' for the given identifier, if its declaring class is a superclass of the current |
| * class. A 'hole' dependency specifies a name that, if filled by something in the library scope, |
| * would require this unit to be recompiled. |
| * |
| * This situation occurs because names in the library scope bind more strongly than unqualified |
| * superclass members. |
| */ |
| private void addHoleIfUnqualifiedSuper(DartIdentifier node, Element holder) { |
| if (isQualified(node)) { |
| return; |
| } |
| if (ElementKind.of(holder) == ElementKind.CLASS && holder != currentClass) { |
| source.addHole(node.getName()); |
| } |
| } |
| |
| /** |
| * Adds a direct dependency on the unit providing given {@link Element}. |
| */ |
| private void addElementDependency(Element element) { |
| DartSource elementSource = (DartSource) element.getSourceInfo().getSource(); |
| if (elementSource != null) { |
| LibrarySource library = elementSource.getLibrary(); |
| if (library != null) { |
| URI libUri = library.getUri(); |
| LibraryDeps.Dependency dep = new LibraryDeps.Dependency(libUri, elementSource.getName(), |
| elementSource.getLastModified()); |
| source.addDep(dep); |
| } |
| } |
| } |
| |
| /** |
| * @return <code>true</code> if given {@link DartIdentifier} is "name" part of qualified property |
| * access or method invocation. |
| */ |
| private static boolean isQualified(DartIdentifier node) { |
| if (node.getParent() instanceof DartPropertyAccess) { |
| return ((DartPropertyAccess) node.getParent()).getName() == node; |
| } |
| if (node.getParent() instanceof DartMethodInvocation) { |
| return ((DartMethodInvocation) node.getParent()).getFunctionName() == node; |
| } |
| return false; |
| } |
| } |