/******************************************************************************* * Copyright (c) 2009, 2014 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 * Dawid PakuĊ‚a [414814] *******************************************************************************/ package org.eclipse.php.internal.core.typeinference; import java.util.*; import java.util.Map.Entry; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.statements.Statement; import org.eclipse.dltk.ti.IContext; import org.eclipse.php.core.compiler.ast.nodes.*; /** * Variable declaration scope. Each scope contains mapping between variable name * and its possible declarations. * * @author michael */ public class DeclarationScope { private Map<String, LinkedList<Declaration>> decls = new HashMap<String, LinkedList<Declaration>>(); private IContext context; private Stack<Statement> innerBlocks = new Stack<Statement>(); public DeclarationScope(IContext context) { this.context = context; } /** * Returns context associated with this scope * * @return */ public IContext getContext() { return context; } /** * This must be called when entering inner conditional block. */ public void enterInnerBlock(Statement s) { if (!innerBlocks.isEmpty() && innerBlocks.peek() == s) { return; } innerBlocks.push(s); } /** * This must be called when exiting inner conditional block. */ public void exitInnerBlock() { if (!innerBlocks.isEmpty()) { innerBlocks.pop(); } } public int getInnerBlockLevel() { return innerBlocks.size(); } /** * Returns all declarations for all variables * * @return */ public Map<String, LinkedList<Declaration>> getAllDeclarations() { return decls; } /** * Returns all possible variable declarations for the given variable name in * current scope. * * @param varName */ public Declaration[] getDeclarations(String varName) { List<Declaration> result = new LinkedList<Declaration>(); LinkedList<Declaration> varDecls = decls.get(varName); if (varDecls != null) { for (Declaration decl : varDecls) { if (decl != null) { result.add(decl); } } } return (Declaration[]) result.toArray(new Declaration[result.size()]); } /** * Adds possible variable declaration * * @param varName * Variable name * @param declNode * AST declaration statement node */ public void addDeclaration(String varName, ASTNode declNode) { LinkedList<Declaration> varDecls = decls.get(varName); if (varDecls == null) { varDecls = new LinkedList<Declaration>(); decls.put(varName, varDecls); } int level = innerBlocks.size(); // TODO check ArrayCreation,ArrayVariableReference // skip all inner conditional blocks statements, since we've reached // a re-declaration here while (varDecls.size() > level + 1) { varDecls.removeLast(); } // preserve place for declarations in outer conditional blocks // in case we haven't reached any declaration untill now while (varDecls.size() < level) { varDecls.addLast(null); } if (declNode instanceof Assignment && (((Assignment) declNode).getVariable() instanceof ArrayVariableReference)) { int index = varDecls.size() - 1; while (index >= 0) { Declaration decl = varDecls.get(index); if (decl instanceof ArrayDeclaration) { ArrayDeclaration arrayDeclaration = (ArrayDeclaration) decl; arrayDeclaration.addDeclaration(declNode); return; } index--; } } if (varDecls.size() > level) { Declaration decl = varDecls.get(level); // The case that the node is inside another node if (decl != null) { if (level > 0) { Statement block = innerBlocks.get(level - 1); if (isInSameBlock(block, decl.getNode(), declNode)) { // replace existing declaration with a new one (leave // 'isGlobal' flag the same) decl.setNode(declNode); return; } } else { // The node is the top node. // replace existing declaration with a new one // (leave // 'isGlobal' flag the same) if (decl instanceof ArrayDeclaration) { decl = new Declaration(declNode instanceof GlobalStatement, declNode); varDecls.set(level, decl); } else if (!(declNode instanceof Assignment && (((Assignment) declNode).getVariable() instanceof ArrayVariableReference))) { // bug 414814 decl.setNode(declNode); } return; } } } // add new declaration if (declNode instanceof Assignment && (((Assignment) declNode).getValue() instanceof ArrayCreation)) { varDecls.addLast(new ArrayDeclaration(declNode instanceof GlobalStatement, declNode)); } else { varDecls.addLast(new Declaration(declNode instanceof GlobalStatement, declNode)); } } public static boolean isInSameBlock(Statement block, ASTNode oldNode, ASTNode newNode) { if (block instanceof IfStatement) { IfStatement ifStatement = (IfStatement) block; Statement oldBlock = getBlock(ifStatement, oldNode); Statement newBlock = getBlock(ifStatement, newNode); if (oldBlock != null && oldBlock == newBlock) { return isInSameBlock(newBlock, oldNode, newNode); } else { return false; } } return true; } private static Statement getBlock(IfStatement ifStatement, ASTNode node) { Statement falseStatement = ifStatement.getFalseStatement(); Statement trueStatement = ifStatement.getTrueStatement(); if (trueStatement != null && trueStatement.sourceStart() <= node.sourceStart() && trueStatement.sourceEnd() >= node.sourceEnd()) { return trueStatement; } else if (falseStatement != null && falseStatement.sourceStart() <= node.sourceStart() && falseStatement.sourceEnd() >= node.sourceEnd()) { return falseStatement; } return null; } public String toString() { StringBuilder buf = new StringBuilder("Variable Declarations (") //$NON-NLS-1$ .append(context).append("): \n\n"); //$NON-NLS-1$ for (Entry<String, LinkedList<Declaration>> entry : decls.entrySet()) { String varName = entry.getKey(); buf.append(varName).append(" => { \n\n"); //$NON-NLS-1$ LinkedList<Declaration> varDecls = entry.getValue(); if (varDecls != null) { for (Declaration declNode : varDecls) { buf.append(declNode.toString()).append(", \n\n"); //$NON-NLS-1$ } } buf.append("}, \n\n"); //$NON-NLS-1$ } return buf.toString(); } }