public class ASTNode {
    
    public static final int BEFORE = 0;
    public static final int AFTER = 1;
    public static final int SAME = 2;
    public static final int CONTAINS = 3;
    public static final int ISCONTAINED = 4;
	
	int beginIndex = Integer.MAX_VALUE;
    int endIndex = Integer.MIN_VALUE;
    private ASTNode parent = null;
    private ASTNode previousSibling = null;
    private ASTNode nextSibling = null;
    
    private int stackDelta = 0;
    
    public ASTNode() {
    	super();
    }
    
    public ASTNode(int theBeginIndex, int theEndIndex) {
        setRange(theBeginIndex, theEndIndex);
    }
    
    /**
     * @return Returns the stackDelta.
     */
    public int getStackDelta() {
        return stackDelta;
    }

    /**
     * @param theStackDelta The stackDelta to set.
     */
    public void setStackDelta(int theStackDelta) {
        stackDelta = theStackDelta;
    }
    
    public void widen(ASTNode node) {
    	leftWiden(node.beginIndex);
    	rightWiden(node.endIndex);
    }
    
    public void leftWiden(int targetBeginIndex) {
    	if (targetBeginIndex < beginIndex) beginIndex = targetBeginIndex;
    }
    
    public void rightWiden(int targetEndIndex) {
    	if (targetEndIndex > endIndex) endIndex = targetEndIndex;
    }
    
    public void setRange(int theBeginIndex, int theEndIndex) {
    	setBeginIndex(theBeginIndex);
        setEndIndex(theEndIndex);
    }

//    private void checkRange() {
//    	if (endIndex!=Integer.MIN_VALUE && beginIndex!=Integer.MAX_VALUE && endIndex<beginIndex) {
//    		throw new RuntimeException("Begin index greater than end index: " + beginIndex + ">" + endIndex);
//    	}
//    }
    
    /** Getter for property beginIndex.
     * @return Value of property beginIndex.
     */
    public int getBeginIndex() {
        return beginIndex;
    }    
    
    /** Setter for property beginIndex.
     * @param theBeginIndex New value of property beginIndex.
     */
    public void setBeginIndex(int theBeginIndex) {
    	beginIndex = theBeginIndex;
    }
    
    /** Getter for property endIndex.
     * @return Value of property endIndex.
     */
    public int getEndIndex() {
        return endIndex;
    }
    
    /** Setter for property endIndex.
     * @param endIndex New value of property endIndex.
     */
    public void setEndIndex(int theEndIndex) {
    	endIndex = theEndIndex;
    }

    public boolean isRightSiblingOf(ASTNode leftSibling) {
        for (ASTNode node=this; node!=null; node=node.getPreviousSibling()) {
	        if (node == leftSibling) {
	            return true;
	        }
	    }
        return false;
    }
    
    public ASTNode rightMostSibling() {
        for (ASTNode node=this;;) {
	        if (node.getNextSibling() == null) {
	            return node;
	        }
	        node = node.getNextSibling();
	    }
    }
	
    public boolean isAncestorOf(ASTNode node) {
    	do {
    		node = node.getParentNode();
    		if (node == this) {
    			return true;
    		}
    	} while (node != null);
    	
    	return false;
    }
    
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName());
        if (getBeginIndex() != Integer.MAX_VALUE) {
            sb.append("[");
            sb.append(getBeginIndex());
            sb.append(", ");
            sb.append(getEndIndex());
            sb.append("]");
        }
        return sb.toString();
    }
    
	/**
	 * @return Returns the parent.
	 */
	public ASTNode getParentNode() {
		return parent;
	}
	
	public Block getParentBlock() {
		return (Block) parent;
	}
    
    public Block getLogicalParentBlock() {
        if (parent != null && parent.parent instanceof IfStatement) {
            return (Block) parent.parent;
        }
        return (Block) parent;
    }

	/**
	 * @param theParent The parent to set.
	 */
	public void setParentNode(ASTNode theParent) {
		parent = theParent;
	}
    
	public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }
	
	/**
     * @return Returns the nextSibling.
     */
    public ASTNode getNextSibling() {
        return nextSibling;
    }
    /**
     * @param theNextSibling The nextSibling to set.
     */
    public void setNextSibling(ASTNode theNextSibling) {
        nextSibling = theNextSibling;
    }
    /**
     * @return Returns the previousSibling.
     */
    public ASTNode getPreviousSibling() {
        return previousSibling;
    }
    /**
     * @param thePreviousSibling The previousSibling to set.
     */
    public void setPreviousSibling(ASTNode thePreviousSibling) {
        previousSibling = thePreviousSibling;
    }
} public interface Assignable {
    
    /**
     * Returns true if the specified Assignable corresponds to this Assignable.
     */
    public boolean isSame(Object obj);
    
} public class Assignment extends Expression {
	
    static public class Operator {
        
        static private HashMap<String, Operator> opsByToken = new HashMap<String, Operator>();
        
        static public Operator lookup(String token) {
            return opsByToken.get(token);
        }
        
        static public Operator ASSIGN = new Operator("=");
        static public Operator PLUS_ASSIGN = new Operator("+=");
        static public Operator MINUS_ASSIGN = new Operator("-=");
        static public Operator TIMES_ASSIGN = new Operator("*=");
        static public Operator DIVIDE_ASSIGN = new Operator("/=");
        static public Operator BIT_AND_ASSIGN = new Operator("&=");
        static public Operator BIT_OR_ASSIGN = new Operator("|=");
        static public Operator BIT_XOR_ASSIGN = new Operator("^=");
        static public Operator REMAINDER_ASSIGN = new Operator("%=");
        static public Operator LEFT_SHIFT_ASSIGN = new Operator("<<=");
        static public Operator RIGHT_SHIFT_SIGNED_ASSIGN = new Operator(">>=");
        static public Operator RIGHT_SHIFT_UNSIGNED_ASSIGN = new Operator(">>>=");
        
        private String token;
        
        Operator(String theToken) {
            token = theToken;
            opsByToken.put(theToken, this);
        }
        
        public String toString() {
            return token;
        }
    }
    
    private Operator operator;
    
	public Assignment(Operator theOperator) {
		super();
		operator = theOperator;
	}
    
	public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }

	/**
	 * @param rightHandSide The rightHandSide to set.
	 */
	public void setRightHandSide(Expression rightHandSide) {
        widen(rightHandSide);
        setChildAt(1, rightHandSide);
	}

	/**
	 * @return Returns the rightHandSide.
	 */
	public Expression getRightHandSide() {
        return (Expression) getChildAt(1);
	}

	/**
	 * @param leftHandSide The leftHandSide to set.
	 */
	public void setLeftHandSide(Expression leftHandSide) {
        setChildAt(0, leftHandSide);
	}

	/**
	 * @return Returns the leftHandSide.
	 */
	public Expression getLeftHandSide() {
        return (Expression) getChildAt(0);
	}
    /**
     * @return Returns the operator.
     */
    public Operator getOperator() {
        return operator;
    }
    /**
     * @param theOerator The operator to set.
     */
    public void setOperator(Operator theOperator) {
        operator = theOperator;
    }
} public class Block extends ASTNode {
    
	public static int TAG = 0;
	
    private String label;
    
    private ASTNode firstChild = null;
    private ASTNode lastChild = null;
    private int childCount = 0;
    
    public Block() {
    	super();
    }
    
    public Block(int theBeginIndex) {
        setBeginIndex(theBeginIndex);
     }
    
    public Block(int theBeginIndex, int theEndIndex) {
        this(theBeginIndex);
        setRange(theBeginIndex, theEndIndex);
    }
    
//    public Block getFooBlock() {
//        Block block = this;
//        while (block!=null) {
//            if (!block.getClass().equals(Block.class)) {
//                return block;
//            }
//            block = block.getParentBlock();
//        }
//        return this;
//    }
    
    public int getTargetPc() {
        if (lastChild instanceof Jump) {
            return ((Jump) lastChild).getTargetIndex();
        }
        return Integer.MAX_VALUE;
    }
    
    public int getTargetIndex() {
        return beginIndex;
    }

    public void setBeginIndex(int theBeginIndex) {
        super.setBeginIndex(theBeginIndex);
    }
    
    public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }

    public void appendChildren(ASTNode begin, ASTNode end) {
    	if (begin == null || end == null) {
        	throw new RuntimeException("Illegal null parameters");
        }
        if (begin.getParentBlock() != null)
            (begin.getParentBlock()).removeChildren(begin, end);

        if (firstChild == null) {
            setFirstChildInternal(begin);
        } else {
            ASTNode prev = getLastChild();
            prev.setNextSibling(begin);
            begin.setPreviousSibling(prev);
        }
        setLastChildInternal(end);

        ASTNode node = begin;
        while (node != null) {
            node.setParentNode(this);
            childCount++;
            if (node == end) break;
            node = node.getNextSibling();
        }    
    }
    
    /**
     * Appends all children of the specified block to this block.
     * @param sourceBlock
     */
    public void appendChildren(Block sourceBlock) {
        if (sourceBlock.getChildCount() > 0) {
            appendChildren(sourceBlock.getFirstChild(), sourceBlock.getLastChild());
        }
    }
    
    /**
     * Sets the specified node at the index. Returns the replaced node, or null if the index
     * was equal to the cild count.
     */
    public ASTNode setChildAt(int index, ASTNode newChild) {
        if (index == childCount) {
            appendChild(newChild);
            return null; 
        } else if (index < 0 || index > childCount) {
            throw new RuntimeException("Index " + index + " out of range [0, " + childCount + "]");
        }
        
        return replaceChild(newChild, getChildAt(index));
    }
    
    public ASTNode getChildAt(int index) {
        if (childCount == 0) {
            throw new RuntimeException("Requested child at index " + index + ", but block has no children");
        }
        if (index < 0 || index >= childCount) {
            throw new RuntimeException("Index " + index + " out of range [0, " + (childCount-1) + "]");
        }
        
        if (index == childCount-1) {
            return getLastChild();
        }
        
        ASTNode node = getFirstChild();
        int i = 0;
        while (i < index) {
            i++;
            node = node.getNextSibling();
        }
        
        return node;
    }
    
    public ASTNode appendChild(ASTNode newChild) {
    	Log.getLogger().debug("Appending " + newChild + " to " + this);
    	
    	unlink(newChild);
        
        if (firstChild == null) {
            newChild.setPreviousSibling(null);
            setFirstChildInternal(newChild);
        } else {
            ASTNode prev = getLastChild();
            prev.setNextSibling(newChild);
            newChild.setPreviousSibling(prev);
        }
        setLastChildInternal(newChild);
        newChild.setParentNode(this);
        childCount++;

    	return newChild;
    }
    
    public ASTNode replaceChild(ASTNode newChild, ASTNode oldChild) {
    	Log.getLogger().debug("Replacing " + oldChild + " by " + newChild + " in " + this);
        if (oldChild.getParentNode() != this) {
            throw new DOMException(DOMException.NOT_FOUND_ERR, "Node " + oldChild + " is not a child of " + this);
        }
        unlink(newChild);
        if (oldChild.getPreviousSibling() != null) {
            oldChild.getPreviousSibling().setNextSibling(newChild);
        }
        if (oldChild.getNextSibling() != null) {
            oldChild.getNextSibling().setPreviousSibling(newChild);
        }
        newChild.setPreviousSibling(oldChild.getPreviousSibling());
	    newChild.setNextSibling(oldChild.getNextSibling());
	    newChild.setParentNode(this);
	    if (getFirstChild() == oldChild) {
	        setFirstChildInternal(newChild);
	    }
	    if (getLastChild() == oldChild) {
	        setLastChildInternal(newChild);
	    }
        oldChild.setPreviousSibling(null);
	    oldChild.setNextSibling(null);
	    oldChild.setParentNode(null);
	    return oldChild;
	}
    
    public ASTNode removeChild(ASTNode oldChild) {
    	Log.getLogger().debug("Removing " + oldChild + " from " + this);
        removeChildren(oldChild, oldChild);
        
	    oldChild.setPreviousSibling(null);
	    oldChild.setNextSibling(null);
        return oldChild;
    }
        
    /**
     * Removes all children from begin to end inclusively.
     */
    private void removeChildren(ASTNode begin, ASTNode end) {
        if (begin == null || end == null) throw new RuntimeException("Illegal null parameters");
    	ASTNode node = begin;
        while (node!= null) {
            if (node.getParentNode() != this) {
                throw new DOMException(DOMException.NOT_FOUND_ERR, "Node " + node + " is not a child of " + this);
            }
            node.setParentNode(null);
            childCount--;
            if (node == end) break;
            node = node.getNextSibling();
        }
        
        
        if (node != end) {
        	throw new RuntimeException("Node " + end + " is not a right-sibling of " + begin);
        }

        if (begin.getPreviousSibling() != null) {
            begin.getPreviousSibling().setNextSibling(end.getNextSibling());
        }
        if (end.getNextSibling() != null) {
            end.getNextSibling().setPreviousSibling(begin.getPreviousSibling());
        }
        if (getFirstChild() == begin) {
	        setFirstChildInternal(end.getNextSibling());
	    }
	    if (getLastChild() == end) {
	        setLastChildInternal(begin.getPreviousSibling());
	    }
    }
    
    public void removeChildren() {
    	if (getFirstChild() != null) {
    		removeChildren(getFirstChild(), getLastChild());
    	}
    }
    
    public int getChildCount() {
        return childCount;
    }
    
    /**
     * Inserts the node newChild after the existing child node refChild.
     * If refChild is null, insert newChild at the beginning of the list of children.  
     * 
     * @param newChild The node to insert
     * @param refChild The reference node, i.e., the node before which the new node must be inserted
     * @return
     */
//    public ASTNode insertAfter(ASTNode newChild, ASTNode refChild) {
//        if (refChild == null) {
//            refChild = getFirstChild();
//        } else {
//            refChild = refChild.getNextSibling();
//        }
//        return insertBefore(newChild, refChild);
//    }

    /**
     * Inserts the node newChild before the existing child node refChild.
     * If refChild is null, insert newChild at the end of the list of children.  
     * 
     * @param newChild The node to insert
     * @param refChild The reference node, i.e., the node before which the new node must be inserted
     * @return
     */
    public ASTNode insertBefore(ASTNode newChild, ASTNode refChild) {
		if (refChild == null) {
            return appendChild(newChild);
        }

    	Log.getLogger().debug("Inserting " + newChild + " before " + refChild + " in " + this);
    	
    	if (refChild.getParentNode() != this) {
            throw new DOMException(DOMException.NOT_FOUND_ERR, "Reference " + refChild + " is not a child of " + this);
        }
        
        unlink(newChild);

        if (refChild.getPreviousSibling() != null) {
            refChild.getPreviousSibling().setNextSibling(newChild);
        }
        newChild.setPreviousSibling(refChild.getPreviousSibling());
        newChild.setNextSibling(refChild);
	    newChild.setParentNode(this);
	    childCount++;
        
        refChild.setPreviousSibling(newChild);
        
	    if (getFirstChild() == refChild) {
	        setFirstChildInternal(newChild);
	    }
	    
        return newChild;
    }
    
    public void addStatements(java.util.List statements) {
        for (int i=0; i<statements.size(); i++) {
            appendChild((ASTNode)statements.get(i));
        }
    }
    
    
    public ASTNode getFirstChild() {
        return firstChild;
    }
    
    public ASTNode getLastChild() {
        return lastChild;
    }
    
    /**
     * @param firstChild The firstChild to set.
     */
    private void setFirstChildInternal(ASTNode newFirstChild) {
        firstChild = newFirstChild;
        if (firstChild != null) {
            firstChild.setPreviousSibling(null);
            beginIndex = Math.min(beginIndex, firstChild.getBeginIndex());
        }
    }

    /**
     * @param lastChild The lastChild to set.
     */
    private void setLastChildInternal(ASTNode newLastChild) {
        lastChild = newLastChild;
        if (lastChild != null) {
            lastChild.setNextSibling(null);
            endIndex = Math.max(endIndex, lastChild.getEndIndex());
        }
    }
    
    /**
     * If the specified node is contained in a tree, remove it.
     */
    private void unlink(ASTNode node) {
        if (node.getParentBlock() != null) {
            node.getParentBlock().removeChild(node);
        }
    }
    
    public String getLabel() {
    	if (label == null) throw new RuntimeException("Statement is not labeled");
        return label;
    }
    
    public void setLabel(String theLabel) {
        label = theLabel;
    }
    
    public boolean isLabeled() {
        return label != null;
    }

    public String setLabeled() {
        if (label == null) label = "L" + (++TAG);
        return label;
    }
} public class Expression extends Block implements Cloneable {
    
    Type type = Type.UNKNOWN;
    
    public Expression() {
        super();
    }
    
    public Expression(int theBeginIndex, int theEndIndex) {
        super(theBeginIndex, theEndIndex);
    }
    
    public Object clone() {
        if (getChildCount() > 0) throw new RuntimeException("Cannot clone expression with children");
        ASTNode node;
        try {
            node = (ASTNode) super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
        node.setParentNode(null);
        node.setPreviousSibling(null);
        node.setNextSibling(null);
        return node;
    }
    
    public Type getTypeBinding() {
        return type;
    }
    
    public void setTypeBinding(Type theType) {
        type = theType;
    }
    
    public int getCategory() {
        if (getTypeBinding().getType() == Type.LONG.getType()) return Form.CATEGORY2;
        if (getTypeBinding().getType() == Type.DOUBLE.getType()) return Form.CATEGORY2;
        return Form.CATEGORY1;
        //return getTypeBinding().getType() == Type.LONG.getType() || getTypeBinding().getType() == Type.DOUBLE.getType()?Form.CATEGORY2:Form.CATEGORY1;
    }

} public abstract class FieldAccess extends Expression {

    private String name;
    private ObjectType type;
    //private MethodDeclaration methodDecl;
    
    public FieldAccess() {
    }
    
    public void initialize(MethodDeclaration methodDecl) {
        Project.getSingleton().addReference(methodDecl, this);
    }
    
    public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }
    
//    if (!faa.getName().equals(fab.getName())) return false;
//    
    public boolean isSame(Object obj) {
        if (!(obj instanceof FieldAccess)) return false;
        FieldAccess other = (FieldAccess) obj;
        if (!name.equals(other.name)) return false;
        if (getExpression() instanceof VariableBinding && other.getExpression() instanceof VariableBinding) {
            VariableBinding vba = (VariableBinding) getExpression();
            VariableBinding vbb = (VariableBinding) other.getExpression();
            return vba.getVariableDeclaration() == vbb.getVariableDeclaration();
        }
        return false;
    }

	/**
	 * @return Returns the expression.
	 */
	public Expression getExpression() {
		return (Expression) getFirstChild();
	}

	/**
	 * @param expression The expression to set.
	 */
	public void setExpression(Expression expression) {
		widen(expression);
		removeChildren();
		appendChild(expression);
	}

	/**
	 * @return Returns the name.
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param theName The name to set.
	 */
	public void setName(String theName) {
		name = theName;
	}
	
	public String toString() {
		return super.toString() + " " + name;
	}

    public ObjectType getType() {
        return type;
    }

    public void setType(ObjectType theType) {
        if (type != null) throw new RuntimeException("Type is already set");
        type = theType;
    }

} public class InfixExpression extends Expression {
	
    static public class Operator {
        static public Operator CONDITIONAL_AND = new Operator("&&");
        static public Operator CONDITIONAL_OR = new Operator("||");
        static public Operator PLUS = new Operator("+");
        static public Operator MINUS = new Operator("-");
        static public Operator TIMES = new Operator("*");
        static public Operator DIVIDE = new Operator("/");
        static public Operator REMAINDER = new Operator("%");
        static public Operator XOR = new Operator("^");
        static public Operator AND = new Operator("&");
        static public Operator OR = new Operator("|");
        static public Operator EQUALS = new Operator("==");
        static public Operator NOT_EQUALS = new Operator("!=");
        static public Operator GREATER_EQUALS = new Operator(">=");
        static public Operator GREATER = new Operator(">");
        static public Operator LESS_EQUALS = new Operator("<=");
        static public Operator LESS = new Operator("<");
        static public Operator RIGHT_SHIFT_SIGNED = new Operator(">>");
        static public Operator LEFT_SHIFT = new Operator("<<");
        static public Operator RIGHT_SHIFT_UNSIGNED = new Operator(">>>");
        
        static {
            EQUALS.complement = NOT_EQUALS;
            NOT_EQUALS.complement = EQUALS;
            GREATER_EQUALS.complement = LESS;
            GREATER.complement = LESS_EQUALS;
            LESS_EQUALS.complement = GREATER;
            LESS.complement = GREATER_EQUALS;
            CONDITIONAL_AND.complement = CONDITIONAL_OR;
            CONDITIONAL_OR.complement = CONDITIONAL_AND;
        }
        
        private String token;
        private Operator complement;
        
        Operator(String theToken) {
            token = theToken;
        }
        
        public String toString() {
            return token;
        }
        
        /**
         * @return Returns the complement.
         */
        public Operator getComplement() {
            return complement;
        }
    }
    
	private Operator operator;
	
	public InfixExpression(Operator op) {
	    super();
	    operator = op;
	    if (operator.getComplement() != null) type = org.apache.bcel.generic.Type.BOOLEAN;
	}
	
	public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }
	
	/**
	 * @return Returns the leftOperand.
	 */
	public Expression getLeftOperand() {
		return (Expression) getChildAt(0);
	}

	/**
	 * @param leftOperand The leftOperand to set.
	 */
	public void setOperands(Expression leftOperand, Expression rightOperand) {
		widen(leftOperand);
        widen(rightOperand);
        removeChildren();
        appendChild(leftOperand);
        appendChild(rightOperand);
	}

	/**
	 * @return Returns the rightOperand.
	 */
	public Expression getRightOperand() {
        return (Expression) getChildAt(1);
	}

    /**
     * @return Returns the operator.
     */
    public Operator getOperator() {
        return operator;
    }

    /**
     * @param theOperator The operator to set.
     */
    public void setOperator(Operator theOperator) {
        operator = theOperator;
    }
} public class MethodDeclaration extends ASTNode {
    
    private String tempPrefix = "_";
    private Block block;
    private Map<String, VariableDeclaration> parameters = new LinkedHashMap<String, VariableDeclaration>();
    private Map<String, VariableDeclaration> localVariables = new HashMap<String, VariableDeclaration>();
    private int accessFlags;
    private Code code;
    private MethodBinding methodBinding;
    private LineNumberCursor lineNumberCursor;

    public MethodDeclaration(MethodBinding theMethodBinding, int theAccessFlags, Code theCode) {
        methodBinding = theMethodBinding;
        accessFlags = theAccessFlags;
        code = theCode;
        lineNumberCursor = new LineNumberCursor(code);
        Project.getSingleton().getOrCreateProcedureUnit(methodBinding);
    }
    
    public int getAccess() {
        return accessFlags;
    }
    
    public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }
    
    public boolean isInstanceConstructor() {
    	return methodBinding.getName().equals("<init>");
    }
	
	/**
     * @return Returns the block.
     */
    public Block getBody() {
        return block;
    }

    /**
     * @param theBlock The block to set.
     */
    public void setBody(Block theBlock) {
        block = theBlock;
        theBlock.setParentNode(this);
    }
    
    public VariableBinding createVariableBinding(String name, Type type, boolean isWrite) {
        if (type == null) throw new NullPointerException();
        
        VariableDeclaration decl = getParameter(name);
        if (decl == null) {
            decl = getLocalVariable(name);
        }
        if (decl == null) {
            decl = new VariableDeclaration(!isWrite);
            decl.setName(name);
            decl.setType(type);
            addLocalVariable(decl);
        }
        
        VariableBinding binding = new VariableBinding(decl);
         
        return binding;
    }
    
    private int vbCount = 0;
    public VariableBinding createAnonymousVariableBinding(Type type, boolean isWrite) {
        String name = tempPrefix + (vbCount++);
        VariableBinding vb = createVariableBinding(name, type, isWrite);
        vb.setTemporary(true);
        return vb;
    }    
    
	public void addParameter(VariableDeclaration variableDecl) {
		parameters.put(variableDecl.getName(), variableDecl);
	}

	public Collection<VariableDeclaration> getParameters() {
		return parameters.values();
	}
	
	public VariableDeclaration getParameter(String name) {
		return parameters.get(name);
	}
	
	public void addLocalVariable(VariableDeclaration variableDecl) {
		localVariables.put(variableDecl.getName(), variableDecl);
	}
	
	public void removeLocalVariable(String name) {
		localVariables.remove(name);
	}

	public Collection<VariableDeclaration> getLocalVariables() {
		return localVariables.values();
	}
	
	public VariableDeclaration getLocalVariable(String name) {
		return localVariables.get(name);
	}

    public MethodBinding getMethodBinding() {
        return methodBinding;
    }
    
    public String toString() {
        return methodBinding.toString();
    }

    public Code getCode() {
        return code;
    }

    public LineNumberCursor getLineNumberCursor() {
        return lineNumberCursor;
    }
} public class NumberLiteral extends Expression {
    
    public static Number ZERO = new Integer(0);
    public static Number ONE = new Integer(1);
    public static Number MINUS_ONE = new Integer(-1);
    
    private Number value;
    
    public static boolean isZero(Expression expr) {
        if (expr == null || !(expr instanceof NumberLiteral)) return false;
        return ((NumberLiteral) expr).getValue().equals(ZERO);
    }
    
    public static boolean isOne(Expression expr) {
        if (expr == null || !(expr instanceof NumberLiteral)) return false;
        return ((NumberLiteral) expr).getValue().equals(ONE);
    }
    
    public static boolean isMinusOne(Expression expr) {
        if (expr == null || !(expr instanceof NumberLiteral)) return false;
        return ((NumberLiteral) expr).getValue().equals(MINUS_ONE);
    }
    
    private NumberLiteral(Number theValue) {
        value = theValue;
        if (theValue instanceof Integer) {
            type = Type.INT;
        } else if (theValue instanceof Byte) {
            type = Type.BYTE;
        } else if (theValue instanceof Float) {
            type = Type.FLOAT;
        } else if (theValue instanceof Double) {
            type = Type.DOUBLE;
        } else if (theValue instanceof Long) {
            type = Type.LONG;
        } else if (theValue instanceof Short) {
            type = Type.SHORT;
        } else
            // TODO: Other types
            throw new RuntimeException("Type not supported: " + theValue.getClass());
    }
    
    public static NumberLiteral create(int i) {
        Number value;
        if (i == 0) value = ZERO;
        else if (i == 1) value = ONE;
        else value = new Integer(i);
        return new NumberLiteral(value);
    }
    
    public static NumberLiteral create(Number value) {
        if (value instanceof Integer) {
            Integer i = (Integer) value;
            if (i.intValue()==0) value = ZERO;
            else if (i.intValue()==1) value = ONE;
        }
        return new NumberLiteral(value);
    }
    
    public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }
    
	/**
	 * @return Returns the value.
	 */
	public Number getValue() {
		return value;
	}

	/**
	 * @param theValue The value to set.
	 */
	public void setValue(Number theValue) {
		value = theValue;
	}
	
	public String toString() {
		return super.toString() + " value " + value;
	}

} public class PStarExpression extends Expression {
	
	// Operators common to both pre- and post-fix expression.
	static public Operator INCREMENT = new Operator("++");
    static public Operator DECREMENT = new Operator("--");
    
    static public class Operator {
        
        private String token;
        
        Operator(String theToken) {
            token = theToken;
        }
        
        public String toString() {
            return token;
        }
        
        public Operator complement() {
            if (this == PStarExpression.INCREMENT) return PStarExpression.DECREMENT;
            else if (this == PStarExpression.DECREMENT) return PStarExpression.INCREMENT;
            else throw new RuntimeException("Operation not supported for " + this);
        }
        
    }
    
    private ASTNode operand;
	private Operator operator;
	
	public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }
	
	/**
	 * @return Returns the operand.
	 */
	public ASTNode getOperand() {
		return operand;
	}

	/**
	 * @param leftOperand The leftOperand to set.
	 */
	public void setOperand(ASTNode theOperand) {
		widen(theOperand);
		operand = theOperand;
	}

    /**
     * @return Returns the operator.
     */
    public Operator getOperator() {
        return operator;
    }

    /**
     * @param theOperator The operator to set.
     */
    public void setOperator(Operator theOperator) {
        operator = theOperator;
    }
} public class PostfixExpression extends PStarExpression {
	
	public PostfixExpression() {
	    super();
	}
	
	public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }
} public class PrefixExpression extends PStarExpression {
	
	static public Operator NOT = new Operator("!");
//    static public Operator INCREMENT = new Operator("++");
//    static public Operator DECREMENT = new Operator("--");
    static public Operator MINUS = new Operator("-");
    static public Operator PLUS = new Operator("+");
    static public Operator COMPLEMENT = new Operator("~");
    
	public PrefixExpression() {
	    super();
	}
	
	public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }
} public class VariableBinding extends Expression implements Assignable {
    private boolean field;
	private VariableDeclaration decl;
	private boolean isTemporary = false;
    
    public static boolean isBoolean(Expression expr) {
        if (expr == null || !(expr instanceof VariableBinding)) return false;
        if (((VariableBinding) expr).getVariableDeclaration().getType() != Type.BOOLEAN) return false;
        return true;
    }
    
	public VariableBinding(VariableDeclaration theDecl) {
		super();
		decl = theDecl;
        decl.vbs.add(this);
		setTypeBinding(theDecl.getType());
	}
    
    public Object clone() {
        // TODO: Make cloning explicit.
        VariableBinding other = (VariableBinding) super.clone();
        decl.vbs.add(other);
        return other;
    }
    
    public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }
    
    public boolean isSame(Object other) {
    	if (other == null || !(other instanceof VariableBinding)) return false;
    	return decl == ((VariableBinding) other).decl;
    }

	/**
	 * @return Returns the name.
	 */
	public String getName() {
		return decl.getName();
	}

	/**
	 * @return Returns the field.
	 */
	public boolean isField() {
		return field;
	}

	/**
	 * @param theField The field to set.
	 */
	public void setField(boolean theField) {
		field = theField;
	}

	/**
	 * @return Returns the decl.
	 */
	public VariableDeclaration getVariableDeclaration() {
		return decl;
	}
    
    public String toString() {
        return super.toString() + ' ' + decl.getName();
    }

    public boolean isTemporary() {
        return isTemporary;
    }

    public void setTemporary(boolean isTemporary) {
        this.isTemporary = isTemporary;
    }
} public class VariableDeclaration extends ASTNode {
	public static int LOCAL = 0;
	public static int NON_LOCAL = 1;
	public static int LOCAL_PARAMETER = 2;
    
    private String name;
	private Type type;
	private int modifiers;
	private int location;
	private boolean isInitialized;
    public List<VariableBinding> vbs = new ArrayList<VariableBinding>();
    
    public static String getLocalVariableName(Method method, int slot, int pc) {
        if (false && method.getLocalVariableTable() != null) {
            // TODO: Use source variable name as comment only.
            LocalVariable[] table = method.getLocalVariableTable().getLocalVariableTable();
            LocalVariable lvar = null;
            for (int i=0; i<table.length; i++) {
                lvar = table[i];
                if (lvar.getIndex() == slot 
                        && lvar.getStartPC() <= pc && pc <= lvar.getStartPC()+lvar.getLength()) {
                    return lvar.getName();
                }
            }
            // if (name.equals("this")) return "l" + slot;
        }
        return "l" + slot;
    }
	
	public VariableDeclaration(boolean theIsInitialized) {
	    location = VariableDeclaration.LOCAL;
	    isInitialized = theIsInitialized;
	}
	
	public VariableDeclaration(int theLocation) {
	    if (theLocation != VariableDeclaration.NON_LOCAL && theLocation != VariableDeclaration.LOCAL_PARAMETER) {
	    	throw new RuntimeException("Illegal location specified: " + theLocation);
	    }
		location = theLocation;
	    isInitialized = (theLocation == VariableDeclaration.NON_LOCAL);
	}
	
	public void visit(AbstractVisitor visitor) {
	    visitor.visit(this);
    }
	
	/**
	 * @return Returns the name.
	 */
	public String getName() {
		return name;
	}
	/**
	 * @param theName The name to set.
	 */
	public void setName(String theName) {
		name = theName;
	}
	/**
	 * @return Returns the modifiers.
	 */
	public int getModifiers() {
		return modifiers;
	}
	/**
	 * @param theModifiers The modifiers to set.
	 */
	public void setModifiers(int theModifiers) {
		modifiers = theModifiers;
	}
    /**
     * @return Returns the type.
     */
    public Type getType() {
        return type;
    }
    /**
     * @param theType The type to set.
     */
    public void setType(Type theType) {
        type = theType;
    }
    /**
     * @return Returns the location.
     */
    public int getLocation() {
        return location;
    }
	/**
	 * Returns true if the variable is initialized during declaration.
	 */
	public boolean isInitialized() {
		return isInitialized;
	}
}
import java.util.Iterator;
import java.util.List;
import org.apache.bcel.generic.Type;
import com.j2js.dom.ASTNode;
import com.j2js.dom.Assignable;
import com.j2js.dom.Assignment;
import com.j2js.dom.Block;
import com.j2js.dom.Expression;
import com.j2js.dom.FieldAccess;
import com.j2js.dom.InfixExpression;
import com.j2js.dom.MethodDeclaration;
import com.j2js.dom.NumberLiteral;
import com.j2js.dom.PStarExpression;
import com.j2js.dom.PostfixExpression;
import com.j2js.dom.PrefixExpression;
import com.j2js.dom.VariableBinding;
import com.j2js.dom.VariableDeclaration;
//                child.getParentBlock().removeChild(child);
//                return next;
//            }
//        }
//        return child;
//    }
    
    private VariableBinding fetchVariableBinding(Expression expr, VariableDeclaration decl) {
        ASTNode child = expr.getFirstChild();
        while (child != null) {
            if (child instanceof VariableBinding && ((VariableBinding) child).getVariableDeclaration() == decl)
                return (VariableBinding) child;
            VariableBinding vb = fetchVariableBinding((Expression) child, decl);
            if (vb != null) return vb;
            child = child.getNextSibling();
        }
        return null;
    }
    
    /**
     * If the specified expression corresponds to
     *      xy + 1 or xy - (-1),
     * returns the INCREMENT operator. If the specified expression corresponds to
     *      xy - 1 or xy + (-1),
     * returns the DECREMENT operator. Otherwise, null is returned.
     * @param expr
     * @return
     */
    private PStarExpression.Operator getOp(InfixExpression expr) {
        NumberLiteral nl;
        if (expr.getRightOperand() instanceof NumberLiteral) {
            nl = (NumberLiteral) expr.getRightOperand();
        } else return null;
        
        PStarExpression.Operator op;
        if (expr.getOperator() == InfixExpression.Operator.PLUS) {
            op = PStarExpression.INCREMENT;
        } else if (expr.getOperator() == InfixExpression.Operator.MINUS) {
            op = PStarExpression.DECREMENT;
        }  else {
            return null;
        }
        
        if (NumberLiteral.isOne(nl)) {
            // We are ok.
        } else if (NumberLiteral.isMinusOne(nl)) {
            op = op.complement();
        } else {
            return null;
        }
        return op;
    }    
    
    /**
     * Reduces
     *      vb = x.y;      (1)
     *      x.y = vb + 1;  (2)
     *      ...
     *      expr(vb);
     * to incremental form
     *       expr(x.y++);
     * Likewise decrement.
     */
    private boolean reduceXCrement(VariableDeclaration decl) {
        Assignment a1 = null;
        Assignment a2 = null;
        VariableBinding vb1 = null;
        VariableBinding vb2 = null;
        
        Assignable fa1 = null;
        Assignable fa2 = null;
        
        InfixExpression sum = null;
        
        
        Iterator iter = decl.vbs.iterator();

        while (iter.hasNext()) {
            VariableBinding vb = (VariableBinding) iter.next();
            
            if (vb.getParentNode() instanceof Assignment) {
                Assignment a = (Assignment) vb.getParentNode();
                if (a.getLeftHandSide() == vb && a.getRightHandSide() instanceof Assignable) {
                    vb1 = vb;
                    a1 = a;
                    fa1 = (Assignable) a.getRightHandSide();
                    continue;
                }
            }

            if (vb.getParentNode() instanceof InfixExpression) {
                InfixExpression infix = (InfixExpression) vb.getParentNode();
                if (infix.getParentNode() instanceof Assignment) {
                    Assignment a = (Assignment) infix.getParentNode();
                    if (a.getLeftHandSide() instanceof Assignable) {
                        vb2 = vb;
                        fa2 = (Assignable) a.getLeftHandSide();
                        a2 = a;
                        sum = infix;
                        continue;
                    }
                }
            }
        }

        if (a1 == null || a2 == null) return false;
        if (!fa1.isSame(fa2)) return false;

        PStarExpression.Operator operator = getOp(sum);
        if (operator == null) return false;
        
        PStarExpression p = new PostfixExpression();
        p.setOperand((Expression) fa1);
        p.setOperator(operator);

        decl.vbs.remove(vb1);
        decl.vbs.remove(vb2);
        VariableBinding vb = decl.vbs.get(0);
        vb.getParentBlock().replaceChild(p, vb);

        Block b = a1.getParentBlock();