/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.core.typeinference;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.declarations.MethodDeclaration;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.declarations.TypeDeclaration;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.ast.references.VariableReference;
import org.eclipse.dltk.ast.statements.Statement;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.ti.IContext;
import org.eclipse.php.core.compiler.ast.nodes.*;
import org.eclipse.php.internal.core.typeinference.context.ContextFinder;
/**
* This visitor builds local variable declarations tree for specified file. The
* resulting tree contains variable declarations list (one per
* {@link DeclarationScope}), where the list entries are possible variable
* declarations. Examples:
* <p>
* 1. Here are two possible declarations for "$a":<br/>
*
* <pre>
* $a = "some";
* if (someCondition()) {
* $a = "other";
* }
* list($a, list($b)) = array(...)
* </pre>
*
* <br/>
* </p>
*
* @author michael
*/
public class VariableDeclarationSearcher extends ContextFinder {
/**
* Scope variable declarations map
*/
private Map<IContext, DeclarationScope> scopes = new HashMap<IContext, DeclarationScope>();
/**
* Stack of processed AST nodes
*/
protected Stack<ASTNode> nodesStack = new Stack<ASTNode>();
public VariableDeclarationSearcher(ISourceModule sourceModule) {
super(sourceModule);
}
/**
* Override to invoke additional processing on this kind of node
*
* @param node
*/
protected void postProcess(ModuleDeclaration node) {
}
public final boolean visit(ModuleDeclaration node) throws Exception {
if (!isInteresting(node)) {
visitGeneral(node);
return false;
}
postProcess(node);
return super.visit(node);
}
public final boolean endvisit(ModuleDeclaration node) throws Exception {
return super.endvisit(node);
}
/**
* Override to invoke additional processing on this kind of node
*
* @param node
*/
protected void postProcess(TypeDeclaration node) {
}
public final boolean visit(TypeDeclaration node) throws Exception {
if (!isInteresting(node)) {
visitGeneral(node);
return false;
}
postProcess(node);
return super.visit(node);
}
public final boolean endvisit(TypeDeclaration node) throws Exception {
return super.endvisit(node);
}
/**
* Override to invoke additional processing on this kind of node
*
* @param node
*/
protected void postProcess(MethodDeclaration node) {
}
public final boolean visit(MethodDeclaration node) throws Exception {
if (!isInteresting(node)) {
visitGeneral(node);
return false;
}
postProcess(node);
return super.visit(node);
}
public final boolean endvisit(MethodDeclaration node) throws Exception {
return super.endvisit(node);
}
/**
* Override to invoke additional processing on this kind of node
*
* @param node
*/
protected void postProcess(Expression node) {
}
public final boolean visit(Expression node) throws Exception {
if (!isInteresting(node)) {
visitGeneral(node);
return false;
}
ASTNode parent = nodesStack.peek();
if (isConditional(parent)) {
getScope().enterInnerBlock((Statement) parent);
}
if (node instanceof Assignment) {
Expression variable = ((Assignment) node).getVariable();
addDeclaredVariables(variable, node);
}
postProcess(node);
return super.visit(node);
}
private void addDeclaredVariables(Expression variable, Expression node) {
if (variable instanceof VariableReference) {
VariableReference varReference = (VariableReference) variable;
getScope().addDeclaration(varReference.getName(), node);
} else if (variable instanceof ListVariable) {
ListVariable varReference = (ListVariable) variable;
for (Expression nestedVar : varReference.getVariables()) {
addDeclaredVariables(nestedVar, node);
}
} else if (variable instanceof ReflectionArrayVariableReference) {
Expression expression = ((ReflectionArrayVariableReference) variable).getExpression();
while (expression instanceof ReflectionArrayVariableReference) {
expression = ((ReflectionArrayVariableReference) expression).getExpression();
}
if (expression instanceof ArrayVariableReference) {
ArrayVariableReference varRef = (ArrayVariableReference) expression;
getScope().addDeclaration(varRef.getName(), node);
}
}
}
public boolean endvisit(Expression node) throws Exception {
return super.endvisit(node);
}
/**
* Override to invoke additional processing on this kind of node
*
* @param node
*/
protected void postProcess(Statement node) {
}
public final boolean visit(Statement node) throws Exception {
if (!isInteresting(node)) {
visitGeneral(node);
return false;
}
ASTNode parent = nodesStack.peek();
if (isConditional(parent)) {
getScope().enterInnerBlock((Statement) parent);
}
if (node instanceof GlobalStatement) {
GlobalStatement globalStatement = (GlobalStatement) node;
for (Expression variable : globalStatement.getVariables()) {
if (variable instanceof VariableReference) {
VariableReference varReference = (VariableReference) variable;
getScope().addDeclaration(varReference.getName(), globalStatement);
}
}
} else if (node instanceof FormalParameter) {
FormalParameter parameter = (FormalParameter) node;
getScope().addDeclaration(parameter.getName(), parameter);
} else if (node instanceof CatchClause) {
CatchClause clause = (CatchClause) node;
VariableReference varReference = clause.getVariable();
getScope().addDeclaration(varReference.getName(), clause);
} else if (node instanceof ForEachStatement) {
ForEachStatement foreachStatement = (ForEachStatement) node;
Expression value = foreachStatement.getValue();
if (value instanceof ReferenceExpression) { // foreach ( $array as
// &$value )
value = ((ReferenceExpression) value).getVariable();
}
if (value instanceof SimpleReference) {
String variableName = ((SimpleReference) value).getName();
getScope().addDeclaration(variableName, foreachStatement);
}
final Expression key = foreachStatement.getKey();
if (key instanceof SimpleReference) {
String variableName = ((SimpleReference) key).getName();
getScope().addDeclaration(variableName, foreachStatement);
}
} else if (node instanceof StaticStatement) {
StaticStatement staticStatement = (StaticStatement) node;
// Collection<? extends Expression> expressions =
// staticStatement.getExpressions();
for (Expression variable : staticStatement.getExpressions()) {
if (variable instanceof VariableReference) {
VariableReference varReference = (VariableReference) variable;
getScope().addDeclaration(varReference.getName(), staticStatement);
}
}
// VariableReference varReference =
// staticStatement.getExpressions();
// getScope().addDeclaration(varReference.getName(), clause);
}
postProcess(node);
return super.visit(node);
}
public final boolean endvisit(Statement node) throws Exception {
if (isConditional(node)) {
getScope().exitInnerBlock();
}
return super.endvisit(node);
}
/**
* Override to invoke additional processing on this kind of node
*
* @param node
*/
protected void postProcessGeneral(ASTNode node) {
}
public final boolean visitGeneral(ASTNode node) throws Exception {
nodesStack.push(node);
postProcessGeneral(node);
return super.visitGeneral(node);
}
public final void endvisitGeneral(ASTNode node) throws Exception {
nodesStack.pop();
super.endvisitGeneral(node);
}
/**
* Returns whether the sub-tree of the given node should be processed. By
* default it well process all nodes.
*
* @param node
* @return
*/
protected boolean isInteresting(ASTNode node) {
return true;
}
/**
* Checks whether the given AST node makes possible conditional branch in
* variables declaration flow.
*
* @param node
* @return
*/
protected boolean isConditional(ASTNode node) {
if (node instanceof Statement) {
final int kind = ((Statement) node).getKind();
return kind == ASTNodeKinds.CATCH_CLAUSE || kind == ASTNodeKinds.IF_STATEMENT
|| kind == ASTNodeKinds.FOR_STATEMENT || kind == ASTNodeKinds.FOR_EACH_STATEMENT
|| kind == ASTNodeKinds.SWITCH_CASE || kind == ASTNodeKinds.WHILE_STATEMENT;
}
return false;
}
/**
* Returns declaration scope for current context
*
* @return
*/
protected DeclarationScope getScope() {
return getScope(contextStack.peek());
}
/**
* Returns declaration scope for given context
*
* @param context
* @return
*/
protected DeclarationScope getScope(IContext context) {
if (!scopes.containsKey(context)) {
scopes.put(context, new DeclarationScope(context));
}
return scopes.get(context);
}
/**
* Returns all declaration scopes
*
* @return
*/
public DeclarationScope[] getScopes() {
Collection<DeclarationScope> values = scopes.values();
return (DeclarationScope[]) values.toArray(new DeclarationScope[values.size()]);
}
/**
* Returns all declarations for the specified variable in the given context
*/
public Declaration[] getDeclarations(String varName, IContext context) {
return getScope(context).getDeclarations(varName);
}
}