Chromium Code Reviews| Index: editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/index/IndexContributor.java |
| diff --git a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/index/IndexContributor.java b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/index/IndexContributor.java |
| index 323fa7c1cb28fd17337cf4c0f52a1808f89bb63c..552a917671dda8823d29a1b3c4b6747886f3ce93 100644 |
| --- a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/index/IndexContributor.java |
| +++ b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/index/IndexContributor.java |
| @@ -14,18 +14,261 @@ |
| package com.google.dart.engine.internal.index; |
| +import com.google.common.annotations.VisibleForTesting; |
| +import com.google.common.collect.Lists; |
| +import com.google.dart.engine.ast.ASTNode; |
| +import com.google.dart.engine.ast.ClassDeclaration; |
| +import com.google.dart.engine.ast.CompilationUnit; |
| +import com.google.dart.engine.ast.ExtendsClause; |
| +import com.google.dart.engine.ast.FunctionDeclaration; |
| +import com.google.dart.engine.ast.FunctionTypeAlias; |
| +import com.google.dart.engine.ast.ImplementsClause; |
| +import com.google.dart.engine.ast.MethodInvocation; |
| +import com.google.dart.engine.ast.PrefixedIdentifier; |
| +import com.google.dart.engine.ast.SimpleIdentifier; |
| +import com.google.dart.engine.ast.TopLevelVariableDeclaration; |
| +import com.google.dart.engine.ast.TypeName; |
| +import com.google.dart.engine.ast.VariableDeclaration; |
| +import com.google.dart.engine.ast.VariableDeclarationList; |
| import com.google.dart.engine.ast.visitor.RecursiveASTVisitor; |
| +import com.google.dart.engine.element.ClassElement; |
| +import com.google.dart.engine.element.CompilationUnitElement; |
| +import com.google.dart.engine.element.Element; |
| +import com.google.dart.engine.element.ElementProxy; |
| +import com.google.dart.engine.element.FieldElement; |
| +import com.google.dart.engine.element.LibraryElement; |
| import com.google.dart.engine.index.IndexStore; |
| +import com.google.dart.engine.index.Location; |
| +import com.google.dart.engine.index.Relationship; |
| +import com.google.dart.engine.source.Source; |
| +import com.google.dart.engine.type.Type; |
| +import com.google.dart.engine.utilities.ast.ASTNodes; |
| +import com.google.dart.engine.utilities.collection.IntStack; |
| + |
| +import java.util.LinkedList; |
| /** |
| * Visits resolved AST and adds relationships into {@link IndexStore}. |
| */ |
| public class IndexContributor extends RecursiveASTVisitor<Void> { |
| + /** |
| + * @return the {@link Location} representing location of the {@link Element}. |
| + */ |
| + @VisibleForTesting |
| + static Location createElementLocation(Element element) { |
| + if (element != null) { |
| + int offset = element.getNameOffset(); |
| + int length = element.getName().length(); |
| + String prefix = null; |
| + return new Location(new ElementProxy(element), offset, length, prefix); |
| + } |
| + return null; |
| + } |
| + |
| + /** |
| + * @return <code>true</code> if given {@link SimpleIdentifier} is "name" part of prefixed |
| + * identifier or method invocation. |
| + */ |
| + private static boolean isQualified(SimpleIdentifier node) { |
| + if (node.getParent() instanceof PrefixedIdentifier) { |
| + return ((PrefixedIdentifier) node.getParent()).getIdentifier() == node; |
| + } |
| + if (node.getParent() instanceof MethodInvocation) { |
| + return ((MethodInvocation) node.getParent()).getMethodName() == node; |
| + } |
| + return false; |
| + } |
| + |
| private final IndexStore store; |
| + private LibraryElement libraryElement; |
| + |
| + /** |
| + * A stack whose top element (the element with the largest index) is an element representing the |
| + * inner-most enclosing scope. |
| + */ |
| + private LinkedList<Element> elementStack = Lists.newLinkedList(); |
| + |
| + /** |
| + * A stack containing one value for each name scope that has been entered, where the values are a |
| + * count of the number of unnamed functions that have been found within that scope. These counts |
| + * are used to synthesize a name for those functions. The innermost scope is at the top of the |
| + * stack. |
| + */ |
| + private IntStack unnamedFunctionCount = new IntStack(); |
| + |
| public IndexContributor(IndexStore store) { |
| this.store = store; |
| } |
| + @Override |
| + public Void visitClassDeclaration(ClassDeclaration node) { |
|
Brian Wilkerson
2013/01/07 21:27:18
This will probably need to be extended to handle m
scheglov
2013/01/07 22:45:41
Done.
|
| + ClassElement element = node.getElement(); |
| + enterScope(element); |
| + try { |
| + recordRelationship( |
| + libraryElement, |
| + IndexConstants.DEFINES_CLASS, |
| + createElementLocation(element)); |
| + { |
| + ExtendsClause extendsClause = node.getExtendsClause(); |
| + if (extendsClause != null) { |
| + TypeName superclassNode = extendsClause.getSuperclass(); |
| + recordSuperType(element, superclassNode, IndexConstants.IS_EXTENDED_BY); |
| + } |
| + } |
| + { |
| + ImplementsClause implementsClause = node.getImplementsClause(); |
| + if (implementsClause != null) { |
| + for (TypeName interfaceNode : implementsClause.getInterfaces()) { |
| + recordSuperType(element, interfaceNode, IndexConstants.IS_IMPLEMENTED_BY); |
| + } |
| + } |
| + } |
| + return super.visitClassDeclaration(node); |
| + } finally { |
| + exitScope(); |
| + } |
| + } |
| + |
| + @Override |
| + public Void visitCompilationUnit(CompilationUnit node) { |
| + CompilationUnitElement unitElement = node.getElement(); |
| + if (unitElement != null) { |
| + elementStack.add(unitElement); |
| + libraryElement = unitElement.getEnclosingElement(); |
| + if (libraryElement != null) { |
| + return super.visitCompilationUnit(node); |
| + } |
| + } |
| + return null; |
| + } |
| + |
| + @Override |
| + public Void visitFunctionDeclaration(FunctionDeclaration node) { |
| + Element element = node.getElement(); |
| + Location location = createElementLocation(element); |
| + recordRelationship(libraryElement, IndexConstants.DEFINES_FUNCTION, location); |
| + return super.visitFunctionDeclaration(node); |
| + } |
| + |
| + @Override |
| + public Void visitFunctionTypeAlias(FunctionTypeAlias node) { |
| + Element element = node.getElement(); |
| + Location location = createElementLocation(element); |
| + recordRelationship(libraryElement, IndexConstants.DEFINES_FUNCTION_TYPE, location); |
| + return super.visitFunctionTypeAlias(node); |
| + } |
| + |
| + @Override |
| + public Void visitSimpleIdentifier(SimpleIdentifier node) { |
| + Element element = node.getElement(); |
| + if (element instanceof FieldElement) { |
| + FieldElement fieldElement = (FieldElement) element; |
| + Location location = createLocation(node); |
| + if (ASTNodes.inGetterContext(node)) { |
| + if (isQualified(node)) { |
| + recordRelationship(fieldElement, IndexConstants.IS_ACCESSED_BY_QUALIFIED, location); |
| + } else { |
| + recordRelationship(fieldElement, IndexConstants.IS_ACCESSED_BY_UNQUALIFIED, location); |
| + } |
| + } else { |
| + throw new UnsupportedOperationException(); |
|
Brian Wilkerson
2013/01/07 21:27:18
I assume we will support this in the future. If so
scheglov
2013/01/07 22:45:41
Done.
|
| + } |
| + } |
| + return super.visitSimpleIdentifier(node); |
| + } |
| + |
| + @Override |
| + public Void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| + VariableDeclarationList variables = node.getVariables(); |
| + for (VariableDeclaration variableDeclaration : variables.getVariables()) { |
| + Element element = variableDeclaration.getElement(); |
| + Location location = createElementLocation(element); |
| + recordRelationship(libraryElement, IndexConstants.DEFINES_VARIABLE, location); |
| + } |
| + return super.visitTopLevelVariableDeclaration(node); |
| + } |
| + |
| + /** |
| + * Enter a new scope represented by the given {@link Element}. |
| + */ |
| + @VisibleForTesting |
| + void enterScope(Element element) { |
| + elementStack.addFirst(element); |
| + unnamedFunctionCount.push(0); |
| + } |
| + |
| +// /** |
| +// * @return the {@link Location} representing the location of the name of the given class. |
| +// */ |
| +// private Location createNameLocation(ClassDeclaration node) { |
| +// return createNameLocation(node.getName()); |
| +// } |
| + |
| + /** |
| + * @return the inner-most enclosing {@link Element}, may be <code>null</code>. |
| + */ |
| + @VisibleForTesting |
| + Element peekElement() { |
| + for (Element element : elementStack) { |
| + if (element != null) { |
| + return element; |
| + } |
| + } |
| + return null; |
| + } |
| + |
| + /** |
| + * @return the {@link Location} representing location of the {@link ASTNode}. |
| + */ |
| + private Location createLocation(ASTNode node) { |
| + String prefix = null; |
|
Brian Wilkerson
2013/01/07 21:27:18
Is this defined because it might someday be non-nu
scheglov
2013/01/07 22:45:41
Yes, it may be not null some day.
Done.
|
| + return createLocation(node.getOffset(), node.getLength(), prefix); |
| + } |
| + |
| + /** |
| + * @param offset the offset of the location within {@link Source} |
| + * @param length the length of the location |
| + * @param prefix the import prefix of top-level element, may be <code>null</code> |
| + * @return the {@link Location} representing the given offset and length within the inner-most |
| + * {@link Element}. |
| + */ |
| + private Location createLocation(int offset, int length, String prefix) { |
| + Element element = peekElement(); |
| + return new Location(new ElementProxy(element), offset, length, prefix); |
| + } |
| + |
| + /** |
| + * Exit the current scope. |
| + */ |
| + private void exitScope() { |
| + elementStack.removeFirst(); |
| + unnamedFunctionCount.pop(); |
| + } |
| + |
| + /** |
| + * Record the given relationship between the given {@link Element} and {@link Location}. |
| + */ |
| + private void recordRelationship(Element element, Relationship relationship, Location location) { |
| + if (element != null && location != null) { |
| + store.recordRelationship(new ElementProxy(element), relationship, location); |
| + } |
| + } |
| + |
| + /** |
| + * Records extends/implements relationships between given {@link ClassElement} and {@link Type} of |
| + * "superNode". |
| + */ |
| + private void recordSuperType(ClassElement element, TypeName superNode, Relationship relationship) { |
| + if (element != null) { |
| + Type superType = superNode.getType(); |
| + if (superType != null) { |
| + Element superElement = superType.getElement(); |
| + recordRelationship(superElement, relationship, createLocation(superNode)); |
| + } |
| + } |
| + } |
| + |
| } |