blob: d0acd51610acb7054a2bb528c8964dafa192e7dc [file] [log] [blame]
// 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;
}
}