/****************************************************************************** * Copyright (c) 2002 - 2014 IBM Corporation. * 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 *****************************************************************************/ package com.ibm.wala.cfg.exc.intra; import com.ibm.wala.dataflow.graph.AbstractMeetOperator; import com.ibm.wala.fixpoint.AbstractVariable; import com.ibm.wala.fixpoint.FixedPointConstants; import com.ibm.wala.fixpoint.UnaryOperator; import com.ibm.wala.ssa.SymbolTable; /** * States for the ssa variables. * * @author Juergen Graf <graf@kit.edu> * */ public class NullPointerState extends AbstractVariable<NullPointerState> { /* * Inital state is UNKNOWN. * Lattice: UNKNOWN < { NULL, NOT_NULL } < BOTH */ public enum State { UNKNOWN, BOTH, NULL, NOT_NULL }; // maps ssa variable number -> State private final State[] vars; NullPointerState(int maxVarNum, SymbolTable symbolTable, ParameterState parameterState) { this.vars = new State[maxVarNum + 1]; // Initialize the states for (int i = 0; i < vars.length; i++) { if (symbolTable.isConstant(i)){ if (symbolTable.isNullConstant(i)){ vars[i] = State.NULL; } else { vars[i] = State.NOT_NULL; } } else { vars[i] = State.UNKNOWN; } } // Add what we know about the parameters (if we know anything about them). // They are the first vars by convention. if (parameterState != null) { for (int i = 0; i < parameterState.getStates().size(); i++){ assert parameterState.getState(i) != null; vars[i + 1] = parameterState.getState(i); assert vars[i + 1] != null; } } } static AbstractMeetOperator<NullPointerState> meetOperator() { return StateMeet.INSTANCE; } /** * This function is not distributive, therefore we cannot use the kildall framework. * <pre> * v3 = phi v1, v2 * ^ := Meet-operator * f := phiValueMeetFunction(3, {1, 2}) = v1,v2,v3 -> v1,v2,[v1 ^ v2] * * f(1,?,?) ^ f(?,1,?) = 1,?,? ^ ?,1,? = 1,1,? * * f(1,?,? ^ ?,1,?) = f(1,1,?) = 1,1,1 * * => f(1,?,? ^ ?,1,?) != f(1,?,?) ^ f(?,1,?) * </pre> */ static UnaryOperator<NullPointerState> phiValueMeetFunction(int varNum, int[] fromVars) { return new PhiValueMeet(varNum, fromVars); } static UnaryOperator<NullPointerState> nullifyFunction(int varNum) { return new NullifyFunction(varNum); } static UnaryOperator<NullPointerState> denullifyFunction(int varNum) { return new DenullifyFunction(varNum); } static UnaryOperator<NullPointerState> identityFunction() { return IndentityFunction.INSTANCE; } boolean isNeverNull(int varNum) { assert varNum > 0 && varNum < vars.length; return vars[varNum] == State.NOT_NULL; } boolean isAlwaysNull(int varNum) { assert varNum > 0 && varNum < vars.length; return vars[varNum] == State.NULL; } /* (non-Javadoc) * @see com.ibm.wala.fixpoint.IVariable#copyState(com.ibm.wala.fixpoint.IVariable) */ @Override public void copyState(NullPointerState v) { assert v.vars.length == vars.length; for (int i = 0; i < v.vars.length; i++) { vars[i] = v.vars[i]; } } /** * This is the meet operator for the NullPointerState variables. * <pre> * ? == unknown, 1 == not null, 0 == null, * == both * * meet | ? | 0 | 1 | * | <- rhs * -----|---|---|---|---| * ? | ? | 0 | 1 | * | * -----|---|---|---|---| * 0 | 0 | 0 | * | * | * -----|---|---|---|---| * 1 | 1 | * | 1 | * | * -----|---|---|---|---| * * | * | * | * | * | * ---------------------- * ^ * | * lhs * </pre> */ boolean meet(final int varNum, final State rhs) { final State lhs = vars[varNum]; if (lhs != State.BOTH && rhs != lhs && rhs != State.UNKNOWN) { if (lhs == State.UNKNOWN) { vars[varNum] = rhs; return true; } else { vars[varNum] = State.BOTH; return true; } } else { return false; } } boolean meet(NullPointerState other) { assert other.vars.length == vars.length; boolean changed = false; for (int i = 0; i < vars.length; i++) { changed |= meet(i, other.vars[i]); } return changed; } /** * This is a intersect operator for the NullPointerState variables. It is used by the * phi values. * <pre> * ? == unknown, 1 == not null, 0 == null, * == both * * meet | ? | 0 | 1 | * | <- rhs * -----|---|---|---|---| * ? | ? | ? | ? | * | * -----|---|---|---|---| * 0 | ? | 0 | * | * | * -----|---|---|---|---| * 1 | ? | * | 1 | * | * -----|---|---|---|---| * * | * | * | * | * | * ---------------------- * ^ * | * lhs * </pre> */ boolean intersect(final int varNum, final State state) { final State lhs = vars[varNum]; if (lhs != State.BOTH && state != lhs) { if (state != State.BOTH && (lhs == State.UNKNOWN || state == State.UNKNOWN)){ vars[varNum] = State.UNKNOWN; return true; } else { vars[varNum] = State.BOTH; return true; } } else { return false; } } boolean nullify(int varNum) { if (vars[varNum] != State.NULL) { vars[varNum] = State.NULL; return true; } return false; } boolean denullify(int varNum) { if (vars[varNum] != State.NOT_NULL) { vars[varNum] = State.NOT_NULL; return true; } return false; } @Override public boolean equals(Object obj) { if (obj instanceof NullPointerState) { NullPointerState other = (NullPointerState) obj; assert vars.length == other.vars.length; for (int i = 0; i < vars.length; i++) { if (vars[i] != other.vars[i]) { return false; } } return true; } return false; } public State getState(int ssaVarNum){ return vars[ssaVarNum]; } @Override public String toString() { StringBuffer buf = new StringBuffer("<"); for (int i = 0; i < vars.length; i++) { switch (vars[i]) { case BOTH: buf.append('*'); break; case NOT_NULL: buf.append('1'); break; case NULL: buf.append('0'); break; case UNKNOWN: buf.append('?'); break; default: throw new IllegalStateException(); } } buf.append('>'); return buf.toString(); } private static class StateMeet extends AbstractMeetOperator<NullPointerState> { private final static StateMeet INSTANCE = new StateMeet(); private StateMeet() {} /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#equals(java.lang.Object) */ @Override public boolean equals(Object o) { return o instanceof StateMeet; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#evaluate(com.ibm.wala.fixpoint.IVariable, com.ibm.wala.fixpoint.IVariable[]) */ @SuppressWarnings("rawtypes") @Override public byte evaluate(NullPointerState lhs, NullPointerState[] rhs) { boolean changed = false; // meet rhs first for (NullPointerState state : rhs) { changed |= lhs.meet(state); } return (changed ? FixedPointConstants.CHANGED : FixedPointConstants.NOT_CHANGED); } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#hashCode() */ @Override public int hashCode() { return 4711; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#toString() */ @Override public String toString() { return "NullPointerStateMeet"; } } private static class PhiValueMeet extends UnaryOperator<NullPointerState> { private final int varNum; private final int[] fromVars; /** * Creates an operator that merges the states of the given variables * fromVars into the state of the phi varaiable varNum * @param varNum Variable number of a phi value * @param fromVars Array of variable numbers the phi value refers to. */ private PhiValueMeet(int varNum, int[] fromVars) { this.varNum = varNum; this.fromVars = fromVars; } @Override public byte evaluate(NullPointerState lhs, NullPointerState rhs) { byte state = FixedPointConstants.NOT_CHANGED; for (int from : fromVars) { if (lhs.intersect(varNum, rhs.vars[from])) { state = FixedPointConstants.CHANGED; } } return state; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#equals(java.lang.Object) */ @Override public boolean equals(Object o) { if (o == this) { return true; } if (o instanceof PhiValueMeet) { PhiValueMeet other = (PhiValueMeet) o; if (varNum == other.varNum && fromVars.length == other.fromVars.length) { for (int i = 0; i < fromVars.length; i++) { if (fromVars[i] != other.fromVars[i]) { return false; } } return true; } } return false; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#hashCode() */ @Override public int hashCode() { return 11000 + varNum; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#toString() */ @Override public String toString() { StringBuffer str = new StringBuffer("Meet(" + varNum + ", ["); for (int i = 0; i < fromVars.length; i++) { str.append(fromVars[i]); str.append(i == fromVars.length - 1 ? "" : ","); } str.append("])"); return str.toString(); } } private static class NullifyFunction extends UnaryOperator<NullPointerState> { private final int varNum; private NullifyFunction(int varNum) { this.varNum = varNum; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.UnaryOperator#evaluate(com.ibm.wala.fixpoint.IVariable, com.ibm.wala.fixpoint.IVariable) */ @Override public byte evaluate(NullPointerState lhs, NullPointerState rhs) { byte state = FixedPointConstants.NOT_CHANGED; if (!lhs.equals(rhs)) { lhs.copyState(rhs); state = FixedPointConstants.CHANGED; } if (lhs.nullify(varNum)) { state = FixedPointConstants.CHANGED; } return state; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#equals(java.lang.Object) */ @Override public boolean equals(Object o) { return o instanceof NullifyFunction && ((NullifyFunction) o).varNum == varNum; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#hashCode() */ @Override public int hashCode() { return 47000 + varNum; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#toString() */ @Override public String toString() { return "Nullify(" + varNum + ")"; } } private static class DenullifyFunction extends UnaryOperator<NullPointerState> { private final int varNum; private DenullifyFunction(int varNum) { assert varNum >= 0; this.varNum = varNum; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.UnaryOperator#evaluate(com.ibm.wala.fixpoint.IVariable, com.ibm.wala.fixpoint.IVariable) */ @Override public byte evaluate(NullPointerState lhs, NullPointerState rhs) { byte state = FixedPointConstants.NOT_CHANGED; if (!lhs.equals(rhs)) { lhs.copyState(rhs); state = FixedPointConstants.CHANGED; } if (lhs.denullify(varNum)) { state = FixedPointConstants.CHANGED; } return state; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#equals(java.lang.Object) */ @Override public boolean equals(Object o) { return o instanceof DenullifyFunction && ((DenullifyFunction) o).varNum == varNum; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#hashCode() */ @Override public int hashCode() { return -47000 - varNum; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#toString() */ @Override public String toString() { return "Denullify(" + varNum + ")"; } } private static class IndentityFunction extends UnaryOperator<NullPointerState> { private static final IndentityFunction INSTANCE = new IndentityFunction(); private IndentityFunction() { } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.UnaryOperator#evaluate(com.ibm.wala.fixpoint.IVariable, com.ibm.wala.fixpoint.IVariable) */ @Override public byte evaluate(NullPointerState lhs, NullPointerState rhs) { if (lhs.equals(rhs)) { return FixedPointConstants.NOT_CHANGED; } else { lhs.copyState(rhs); return FixedPointConstants.CHANGED; } } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#equals(java.lang.Object) */ @Override public boolean equals(Object o) { return o instanceof IndentityFunction; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#hashCode() */ @Override public int hashCode() { return 8911; } /* (non-Javadoc) * @see com.ibm.wala.fixedpoint.impl.AbstractOperator#toString() */ @Override public String toString() { return "Id"; } } }