/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.engine.internal.resolver;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.ClassDeclaration;
import com.google.dart.engine.ast.ClassTypeAlias;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.ast.ConstructorDeclaration;
import com.google.dart.engine.ast.Declaration;
import com.google.dart.engine.ast.FunctionDeclaration;
import com.google.dart.engine.ast.FunctionTypeAlias;
import com.google.dart.engine.ast.MethodDeclaration;
import com.google.dart.engine.context.AnalysisException;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.LibraryElement;
import com.google.dart.engine.error.AnalysisErrorListener;
import com.google.dart.engine.internal.scope.Scope;
import com.google.dart.engine.internal.scope.ScopeBuilder;
import com.google.dart.engine.source.Source;
/**
* Instances of the class {@code IncrementalResolver} resolve the smallest portion of an AST
* structure that we currently know how to resolve.
*/
public class IncrementalResolver {
/**
* The element for the library containing the compilation unit being visited.
*/
private LibraryElement definingLibrary;
/**
* The source representing the compilation unit being visited.
*/
private Source source;
/**
* The object used to access the types from the core library.
*/
private TypeProvider typeProvider;
/**
* The error listener that will be informed of any errors that are found during resolution.
*/
private AnalysisErrorListener errorListener;
/**
* Initialize a newly created incremental resolver to resolve a node in the given source in the
* given library, reporting errors to the given error listener.
*
* @param definingLibrary the element for the library containing the compilation unit being
* visited
* @param source the source representing the compilation unit being visited
* @param typeProvider the object used to access the types from the core library
* @param errorListener the error listener that will be informed of any errors that are found
* during resolution
*/
public IncrementalResolver(LibraryElement definingLibrary, Source source,
TypeProvider typeProvider, AnalysisErrorListener errorListener) {
this.definingLibrary = definingLibrary;
this.source = source;
this.typeProvider = typeProvider;
this.errorListener = errorListener;
}
/**
* Resolve the given node, reporting any errors or warnings to the given listener.
*
* @param node the root of the AST structure to be resolved
* @throws AnalysisException if the node could not be resolved
*/
public void resolve(AstNode node) throws AnalysisException {
AstNode rootNode = findResolutionRoot(node);
Scope scope = ScopeBuilder.scopeFor(rootNode, errorListener);
if (elementModelChanged(rootNode.getParent())) {
throw new AnalysisException("Cannot resolve node: element model changed");
}
resolveTypes(node, scope);
resolveVariables(node, scope);
resolveReferences(node, scope);
}
/**
* Return {@code true} if the given node can be resolved independently of any other nodes.
* <p>
* <b>Note:</b> This method needs to be kept in sync with {@link ScopeBuilder#scopeForAstNode}.
*
* @param node the node being tested
* @return {@code true} if the given node can be resolved independently of any other nodes
*/
private boolean canBeResolved(AstNode node) {
return node instanceof ClassDeclaration || node instanceof ClassTypeAlias
|| node instanceof CompilationUnit || node instanceof ConstructorDeclaration
|| node instanceof FunctionDeclaration || node instanceof FunctionTypeAlias
|| node instanceof MethodDeclaration;
}
/**
* Return {@code true} if the portion of the element model defined by the given node has changed.
*
* @param node the node defining the portion of the element model being tested
* @return {@code true} if the element model defined by the given node has changed
* @throws AnalysisException if the correctness of the element model cannot be determined
*/
private boolean elementModelChanged(AstNode node) throws AnalysisException {
Element element = getElement(node);
if (element == null) {
throw new AnalysisException("Cannot resolve node: a " + node.getClass().getSimpleName()
+ " does not define an element");
}
DeclarationMatcher matcher = new DeclarationMatcher();
return !matcher.matches(node, element);
}
/**
* Starting at the given node, find the smallest AST node that can be resolved independently of
* any other nodes. Return the node that was found.
*
* @param node the node at which the search is to begin
* @return the smallest AST node that can be resolved independently of any other nodes
* @throws AnalysisException if there is no such node
*/
private AstNode findResolutionRoot(AstNode node) throws AnalysisException {
AstNode result = node;
AstNode parent = result.getParent();
while (parent != null && !canBeResolved(parent)) {
result = parent;
parent = result.getParent();
}
if (parent == null) {
throw new AnalysisException("Cannot resolve node: no resolvable node");
}
return result;
}
/**
* Return the element defined by the given node, or {@code null} if the node does not define an
* element.
*
* @param node the node defining the element to be returned
* @return the element defined by the given node
*/
private Element getElement(AstNode node) {
if (node instanceof Declaration) {
return ((Declaration) node).getElement();
} else if (node instanceof CompilationUnit) {
return ((CompilationUnit) node).getElement();
}
return null;
}
private void resolveReferences(AstNode node, Scope scope) {
ResolverVisitor visitor = new ResolverVisitor(
definingLibrary,
source,
typeProvider,
scope,
errorListener);
node.accept(visitor);
}
private void resolveTypes(AstNode node, Scope scope) {
TypeResolverVisitor visitor = new TypeResolverVisitor(
definingLibrary,
source,
typeProvider,
scope,
errorListener);
node.accept(visitor);
}
private void resolveVariables(AstNode node, Scope scope) {
VariableResolverVisitor visitor = new VariableResolverVisitor(
definingLibrary,
source,
typeProvider,
scope,
errorListener);
node.accept(visitor);
}
}