package soot.dava.toolkits.base.AST.structuredAnalysis;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 1997 - 2018 Raja Vallée-Rai and others
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.IntType;
import soot.Local;
import soot.LongType;
import soot.PrimType;
import soot.ShortType;
import soot.SootField;
import soot.Type;
import soot.Value;
import soot.dava.DavaFlowAnalysisException;
import soot.dava.internal.AST.ASTBinaryCondition;
import soot.dava.internal.AST.ASTCondition;
import soot.dava.internal.AST.ASTIfElseNode;
import soot.dava.internal.AST.ASTIfNode;
import soot.dava.internal.AST.ASTMethodNode;
import soot.dava.internal.AST.ASTUnaryBinaryCondition;
import soot.dava.internal.AST.ASTUnaryCondition;
import soot.dava.internal.javaRep.DNotExpr;
import soot.dava.toolkits.base.AST.interProcedural.ConstantFieldValueFinder;
import soot.jimple.BinopExpr;
import soot.jimple.ConditionExpr;
import soot.jimple.DefinitionStmt;
import soot.jimple.FieldRef;
import soot.jimple.Stmt;

public class CP extends StructuredAnalysis {
  /*
   * Constant Propagation:
   *
   * Step 1: Sets of CPTuple (className, CPVariable, value) where: CPVariable contains a local or SootField
   *
   * Step 2: A local or SootField has a constant value at a program point p if on all program paths from the start of the
   * method to point p the local or Sootfield has only been assigned this constant value.
   *
   * Step 3: Forward Analysis
   *
   * Step 4: Intersection (See intersection method in CPFlowSet)
   *
   * Step 5: See method processStatement
   *
   * Step 6: out(start) = all locals set to bottom(they shouldnt be present in the initialSet all formals set to Top, all
   * constant fields set to constant values
   *
   * newInitialFlow: all locals and formals set to Top, all constant fields set to constant values remember new InitialFlow
   * is ONLY used for input to catchBodies
   *
   * Any local or field which is not in the flow set at any time is necessarily bottom
   *
   *
   * knowing how a condition evaluates can give us useful insight to the values of variables Handle this for a == b where
   * both a and b are primtype and special case handle it for A where A is a unary boolean condition
   *
   * See following over-riden methods:
   *
   * public Object processASTIfElseNode(ASTIfElseNode node,Object input) public Object processASTIfNode(ASTIfNode node,Object
   * input) public Object processASTSwitchNode(ASTSwitchNode node,Object input)
   */

  ArrayList<CPTuple> constantFieldTuples = null; // VariableTuples of
  // constantFields
  ArrayList<CPTuple> formals = null; // VariableTuples for formals initially
  // set to T
  ArrayList<CPTuple> locals = null; // VariableTuples for locals initially set
  // to default

  ArrayList<CPTuple> initialInput = null; // VariableTuples of constantFields,
  // locals set to 0 and formals set
  // to T

  ASTMethodNode methodNode = null;
  String localClassName = null;

  /*
   * The start of the analysis takes place whenever this constructor is invoked
   */
  public CP(ASTMethodNode analyze, HashMap<String, Object> constantFields,
      HashMap<String, SootField> classNameFieldNameToSootFieldMapping) {
    super();
    /*
     * DEBUG = true; DEBUG_IF = true; DEBUG_WHILE = true; DEBUG_STATEMENTS = true;
     */
    this.methodNode = analyze;
    localClassName = analyze.getDavaBody().getMethod().getDeclaringClass().getName();

    // Create a list of VariableValueTuples for all the constantFields
    createConstantFieldsList(constantFields, classNameFieldNameToSootFieldMapping);

    // Create the complete list of vars to go into constant propagation
    createInitialInput();

    // input to constant propagation should not be an empty flow set it is
    // the set of all constant fields, locals assigned 0 and formals
    // assigned T
    CPFlowSet initialSet = new CPFlowSet();
    Iterator<CPTuple> it = initialInput.iterator();
    while (it.hasNext()) {
      initialSet.add(it.next());
    }

    // System.out.println("Initial set"+initialSet.toString());
    CPFlowSet result = (CPFlowSet) process(analyze, initialSet);
    // System.out.println("Last result :"+result.toString());
  }

  /*
   * constant fields added with KNOWN CONSTANT VALUE formals added with TOP locals added with 0 other fields IGNORED
   */
  public void createInitialInput() {
    initialInput = new ArrayList<CPTuple>();

    // adding constant fields
    initialInput.addAll(constantFieldTuples);

    // String className =
    // analyze.getDavaBody().getMethod().getDeclaringClass().getName();

    // adding formals
    formals = new ArrayList<CPTuple>();
    // System.out.println("Adding following formals: with TOP");
    Collection col = methodNode.getDavaBody().get_ParamMap().values();
    Iterator it = col.iterator();
    while (it.hasNext()) {
      Object temp = it.next();
      if (temp instanceof Local) {
        Local tempLocal = (Local) temp;
        if (!(tempLocal.getType() instanceof PrimType)) {
          continue;
        }

        CPVariable newVar = new CPVariable(tempLocal);

        // new tuple set to top since this is a formal and we dont know
        // what value we will get into it
        CPTuple newTuple = new CPTuple(localClassName, newVar, true);
        initialInput.add(newTuple);
        formals.add(newTuple);
        // System.out.print("\t"+tempLocal.getName());
      }
    }
    // System.out.println();

    // adding locals
    List decLocals = methodNode.getDeclaredLocals();
    it = decLocals.iterator();
    locals = new ArrayList<CPTuple>();
    // System.out.println("Adding following locals with default values:");
    while (it.hasNext()) {
      Object temp = it.next();
      if (temp instanceof Local) {
        Local tempLocal = (Local) temp;
        Type localType = tempLocal.getType();

        if (!(localType instanceof PrimType)) {
          continue;
        }

        CPVariable newVar = new CPVariable(tempLocal);

        // store the default value into this object
        Object value;

        // locals value is set to the default value that it can have
        // depending on its type
        if (localType instanceof BooleanType) {
          value = new Boolean(false);
        } else if (localType instanceof ByteType) {
          value = new Integer(0);
        } else if (localType instanceof CharType) {
          value = new Integer(0);
        } else if (localType instanceof DoubleType) {
          value = new Double(0);
        } else if (localType instanceof FloatType) {
          value = new Float(0);
        } else if (localType instanceof IntType) {
          value = new Integer(0);
        } else if (localType instanceof LongType) {
          value = new Long(0);
        } else if (localType instanceof ShortType) {
          value = new Integer(0);
        } else {
          throw new DavaFlowAnalysisException("Unknown PrimType");
        }

        CPTuple newTuple = new CPTuple(localClassName, newVar, value);

        /*
         * Commenting the next line since we dont want initial Input to have any locals in it all locals are considered
         * bottom initially
         */
        // initialInput.add(newTuple);

        locals.add(newTuple);
        // System.out.print("\t"+tempLocal.getName());
      } // was a local
    }
    // System.out.println();
  }

  /*
   * Uses the results of the ConstantValueFinder to create a list of constantField CPTuple
   */
  private void createConstantFieldsList(HashMap<String, Object> constantFields,
      HashMap<String, SootField> classNameFieldNameToSootFieldMapping) {
    constantFieldTuples = new ArrayList<CPTuple>();

    Iterator<String> it = constantFields.keySet().iterator();
    // System.out.println("Adding constant fields to initial set: ");
    while (it.hasNext()) {
      String combined = it.next();

      int temp = combined.indexOf(ConstantFieldValueFinder.combiner, 0);
      if (temp > 0) {
        String className = combined.substring(0, temp);

        // String fieldName = combined.substring(temp+
        // ConstantFieldValueFinder.combiner.length());
        SootField field = classNameFieldNameToSootFieldMapping.get(combined);
        // String fieldName = field.getName();

        if (!(field.getType() instanceof PrimType)) {
          // we only care about PrimTypes
          continue;
        }

        // object type is double float long boolean or integer
        Object value = constantFields.get(combined);

        CPVariable var = new CPVariable(field);

        CPTuple newTuples = new CPTuple(className, var, value);
        constantFieldTuples.add(newTuples);
        // System.out.print("Class: "+className +
        // " Field: "+fieldName+" Value: "+value+" ");
      } else {
        throw new DavaFlowAnalysisException("Second argument of VariableValuePair not a variable");
      }

    }
    // System.out.println("");
  }

  @Override
  public DavaFlowSet emptyFlowSet() {
    return new CPFlowSet();
  }

  /*
   * Setting the mergetype to intersection but since we are going to have constantpropagation flow sets the intersection
   * method for this flow set will be invoked which defines the correct semantics of intersection for the case of constant
   * propagation
   */
  public void setMergeType() {
    MERGETYPE = INTERSECTION;
  }

  /*
   * newInitialFlow is invoked for the input set of the catchBodies
   *
   * formals initialized to top since we dont know what has happened so far in the body locals initialized to top since we
   * dont know what has happened so far in the method body constant fields present with their constant value since that never
   * changes
   */
  @Override
  public DavaFlowSet newInitialFlow() {
    CPFlowSet flowSet = new CPFlowSet();

    // formals and locals should be both initialized to top since we dont
    // know what has happened so far in the body
    ArrayList<CPTuple> localsAndFormals = new ArrayList<CPTuple>();
    localsAndFormals.addAll(formals);
    localsAndFormals.addAll(locals);

    Iterator<CPTuple> it = localsAndFormals.iterator();
    while (it.hasNext()) {
      CPTuple tempTuple = (CPTuple) it.next().clone();

      // just making sure all are set to Top
      if (!tempTuple.isTop()) {
        tempTuple.setTop();
      }

      flowSet.add(tempTuple);
    }

    // constant fields should be present with their constant value since
    // that never changes
    it = constantFieldTuples.iterator();
    while (it.hasNext()) {
      flowSet.add(it.next());
    }

    return flowSet;
  }

  @Override
  public DavaFlowSet cloneFlowSet(DavaFlowSet flowSet) {
    if (flowSet instanceof CPFlowSet) {
      return ((CPFlowSet) flowSet).clone();
    } else {
      throw new RuntimeException("cloneFlowSet not implemented for other flowSet types" + flowSet.toString());
    }
  }

  /*
   * Since we are only keeping track of constant fields now there will be no kill if we were keeping track of fields then we
   * woul dneed to kill all non constant fields if there was an invokeExpr
   */
  @Override
  public DavaFlowSet processUnaryBinaryCondition(ASTUnaryBinaryCondition cond, DavaFlowSet input) {
    if (!(input instanceof CPFlowSet)) {
      throw new RuntimeException("processCondition is not implemented for other flowSet types" + input.toString());
    }
    CPFlowSet inSet = (CPFlowSet) input;
    return inSet;
  }

  /*
   * Has no effect whatsoever on the analysis
   */
  @Override
  public DavaFlowSet processSynchronizedLocal(Local local, DavaFlowSet input) {
    if (!(input instanceof CPFlowSet)) {
      throw new RuntimeException("processSynchronized  is not implemented for other flowSet types" + input.toString());
    }
    DavaFlowSet inSet = (DavaFlowSet) input;
    return inSet;

  }

  /*
   * no effect on the analysis since we are keeping track of non constant fields only if we were tracking other fields then
   * If the value contained an invoke expr top all non constant fields similar to as done for unarybinary condition
   */
  @Override
  public DavaFlowSet processSwitchKey(Value key, DavaFlowSet input) {
    if (!(input instanceof CPFlowSet)) {
      throw new RuntimeException("processCondition is not implemented for other flowSet types" + input.toString());
    }
    CPFlowSet inSet = (CPFlowSet) input;
    return inSet;

  }

  /*
   * x = expr;
   *
   * expr is a constant
   *
   * epr is a local (Note we are not going to worry about fieldRefs)
   *
   * expr is a Unary op with neg followed by a local or field
   *
   * expr is actually exp1 op expr2 ..... ( x = x+1, y = i * 3, x = 3 *0
   *
   * expr contains an invokeExpr
   */
  @Override
  public DavaFlowSet processStatement(Stmt s, DavaFlowSet input) {
    if (!(input instanceof CPFlowSet)) {
      throw new RuntimeException("processStatement is not implemented for other flowSet types");
    }

    CPFlowSet inSet = (CPFlowSet) input;
    if ((inSet == NOPATH) || !(s instanceof DefinitionStmt)) {
      return inSet;
    }

    DefinitionStmt defStmt = (DefinitionStmt) s;
    // x = expr;
    // confirm that the left side is a local with a primitive type
    Value left = defStmt.getLeftOp();
    if (!(left instanceof Local && ((Local) left).getType() instanceof PrimType)) {
      return inSet;
    }

    // left is a primitive primitive local
    CPFlowSet toReturn = (CPFlowSet) cloneFlowSet(inSet);

    /*
     * KILL ANY PREVIOUS VALUE OF this local as this is an assignment Remember the returned value can be null if the element
     * was not found or it was TOP
     */
    Object killedValue = killButGetValueForUse((Local) left, toReturn);

    Value right = defStmt.getRightOp();

    Object value = CPHelper.isAConstantValue(right);
    if (value != null) {
      // EXPR IS A CONSTANT
      if (left.getType() instanceof BooleanType) {
        Integer tempValue = (Integer) value;
        if (tempValue.intValue() == 0) {
          value = new Boolean(false);
        } else {
          value = new Boolean(true);
        }
      }
      addOrUpdate(toReturn, (Local) left, value);
    } else {
      // EXPR IS NOT A CONSTANT
      handleMathematical(toReturn, (Local) left, right, killedValue);
    }
    return toReturn;
  }

  /*
   * The returned value is the current constant associated with left. Integer/Long/Double/Float/Boolean The returned value
   * can also be null if the element was not found or it was TOP
   */
  public Object killButGetValueForUse(Local left, CPFlowSet toReturn) {

    for (CPTuple tempTuple : toReturn) {
      if (!(tempTuple.getSootClassName().equals(localClassName))) {
        continue;
      }

      // className is the same check if the variable is the same or not
      if (tempTuple.containsLocal()) { // remmeber the sets contain
        // constant fields also
        Local tempLocal = tempTuple.getVariable().getLocal();
        if (left.getName().equals(tempLocal.getName())) {
          // KILL THIS SUCKA!!!
          Object killedValue = tempTuple.getValue();
          tempTuple.setTop();
          return killedValue;
        }
      }
    } // going through all elements of the flow set

    // if this element was no where enter it with top
    CPVariable newVar = new CPVariable(left);

    // create the CPTuple
    // System.out.println("trying to kill something which was not present so added with TOP");
    CPTuple newTuple = new CPTuple(localClassName, newVar, false);
    toReturn.add(newTuple);
    return null;
  }

  /*
   * Create a CPTuple for left with the val and update the toReturn set
   */
  private void addOrUpdate(CPFlowSet toReturn, Local left, Object val) {
    CPVariable newVar = new CPVariable(left);

    CPTuple newTuple = new CPTuple(localClassName, newVar, val);
    toReturn.addIfNotPresent(newTuple);
    // System.out.println("DefinitionStmt checked right expr for constants"+toReturn.toString());
  }

  /*
   * x = b where b is in the before set of the statement as a constant then we can simply say x = that constant also
   *
   * TODO: DONT WANT TO DO IT:::: If right expr is a unary expression see if the stuff inside is a Local
   *
   * x = exp1 op exp2 (check if both exp1 and exp2 are int constants
   *
   * killedValuse is either the constant value which left had before this assignment stmt or null if left was Top or not in
   * the set
   *
   * handle the special case when the inputset could not find a value because its the killed value //eg. x = x+1 since we top
   * x first we will never get a match IMPORTANT
   */
  private void handleMathematical(CPFlowSet toReturn, Local left, Value right, Object killedValue) {

    // if right expr is a local or field
    Object value = isANotTopConstantInInputSet(toReturn, right);
    if (value != null) {
      // right was a local or field with a value other than top
      // dont send value SEND A CLONE OF VALUE.....IMPORTANT!!!!
      Object toSend = CPHelper.wrapperClassCloner(value);

      if (toSend != null) {
        addOrUpdate(toReturn, left, toSend);
      }

      // return if value was != null as this means that the left was a
      // primitive local assigned some value from the right
      return;
    }

    // if we get here we know that right is not a local or field whose value
    // we could find in the set
    if (right instanceof BinopExpr) {
      Value op1 = ((BinopExpr) right).getOp1();
      Value op2 = ((BinopExpr) right).getOp2();

      Object op1Val = CPHelper.isAConstantValue(op1);
      Object op2Val = CPHelper.isAConstantValue(op2);

      if (op1Val == null) {
        op1Val = isANotTopConstantInInputSet(toReturn, op1);
      }

      if (op2Val == null) {
        op2Val = isANotTopConstantInInputSet(toReturn, op2);
      }

      if (op1 == left) {
        // System.out.println("\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>OP1 is the same as LHS");
        op1Val = killedValue;
      }

      if (op2 == left) {
        // System.out.println("\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>OP2 is the same as LHS");
        op2Val = killedValue;
      }

      if (op1Val != null && op2Val != null) {
        // System.out.println("found constant values for both operands of binary expression");
        if (left.getType() instanceof IntType && op1Val instanceof Integer && op2Val instanceof Integer) {
          // only caring about operations on two integers and result
          // is an integer

          int op1IntValue = ((Integer) op1Val).intValue();
          int op2IntValue = ((Integer) op2Val).intValue();

          String tempStr = ((BinopExpr) right).getSymbol();
          if (tempStr.length() > 1) {
            char symbol = tempStr.charAt(1);
            // System.out.println("found symbol "+symbol+" for the operands of binary expression");
            int newValue = 0;
            boolean set = false;
            switch (symbol) {
              case '+':
                // System.out.println("Adding");
                newValue = op1IntValue + op2IntValue;
                set = true;
                break;
              case '-':
                // System.out.println("Subtracting");
                newValue = op1IntValue - op2IntValue;
                set = true;

                break;

              case '*':
                // System.out.println("Multiplying");
                newValue = op1IntValue * op2IntValue;
                set = true;

                break;
            }

            if (set) {
              // we have our new value
              Integer newValueObject = new Integer(newValue);
              addOrUpdate(toReturn, left, newValueObject);
              return;
            }
          }
        }
      } else {
        // System.out.println("atleast one value is not constant so cant simplify expression");
      }
    }
    // System.out.println("DefinitionStmt checked right expr for mathematical stuff"+toReturn.toString());
  }

  /*
   * Check whether it is a local or field which has a constant value (could be top) in the current inSet
   *
   * The method returns null if its not found or its TOP Otherwise it will return the constant value
   */
  private Object isANotTopConstantInInputSet(CPFlowSet set, Value toCheck) {
    if (toCheck instanceof Local || toCheck instanceof FieldRef) {
      String toCheckClassName = null;
      if (toCheck instanceof Local) {
        toCheckClassName = localClassName;
      } else {
        toCheckClassName = ((FieldRef) toCheck).getField().getDeclaringClass().getName();
      }

      for (CPTuple tempTuple : set) {
        // check that the classNames are the same
        if (!(tempTuple.getSootClassName().equals(toCheckClassName))) {
          // classNames are not the same no point in continuing with
          // checks
          continue;
        }

        boolean tupleFound = false;
        if (tempTuple.containsLocal() && toCheck instanceof Local) {
          // check they are the same Local
          Local tempLocal = tempTuple.getVariable().getLocal();
          if (tempLocal.getName().equals(((Local) toCheck).getName())) {
            // the set does have a constant value for this local
            tupleFound = true;
          }
        } else if (tempTuple.containsField() && toCheck instanceof FieldRef) {
          SootField toCheckField = ((FieldRef) toCheck).getField();
          SootField tempField = tempTuple.getVariable().getSootField();

          if (tempField.getName().equals(toCheckField.getName())) {
            // the set contains a constant value for this field
            tupleFound = true;
          }
        }

        if (tupleFound) {
          if (tempTuple.isTop()) {
            return null;
          } else {
            return tempTuple.getValue();
          }
        }
      }

    }
    return null;
  }

  /*
   * over-riding the StructuredFlow Analysis implementation because we want to be able to gather information about the truth
   * value in the condition
   */
  @Override
  public DavaFlowSet processASTIfNode(ASTIfNode node, DavaFlowSet input) {
    if (DEBUG_IF) {
      System.out.println("Processing if node using over-ridden process if method" + input.toString());
    }

    input = processCondition(node.get_Condition(), input);

    if (!(input instanceof CPFlowSet)) {
      throw new DavaFlowAnalysisException("not a flow set");
    }

    CPFlowSet inputToBody = ((CPFlowSet) input).clone();

    CPTuple tuple = checkForValueHints(node.get_Condition(), inputToBody, false);

    if (tuple != null) {
      // if not null, is a belief going into the if branch simply add it
      // into the input set
      // System.out.println(">>>>>Adding tuple because of condition"+tuple.toString());
      inputToBody.addIfNotPresentButDontUpdate(tuple);
    }

    DavaFlowSet output1 = processSingleSubBodyNode(node, inputToBody);

    if (DEBUG_IF) {
      System.out.println("\n\nINPUTS TO MERGE ARE input (original):" + input.toString() + "processingBody output:"
          + output1.toString() + "\n\n\n");
    }

    // merge with input which tells if the cond did not evaluate to true
    DavaFlowSet output2 = merge(input, output1);

    // handle break
    String label = getLabel(node);

    DavaFlowSet temp = handleBreak(label, output2, node);

    if (DEBUG_IF) {
      System.out.println("Exiting if node" + temp.toString());
    }

    return temp;
  }

  @Override
  public DavaFlowSet processASTIfElseNode(ASTIfElseNode node, DavaFlowSet input) {
    if (DEBUG_IF) {
      System.out.println("Processing IF-ELSE node using over-ridden process if method" + input.toString());
    }

    if (!(input instanceof CPFlowSet)) {
      throw new DavaFlowAnalysisException("not a flow set");
    }

    // get the subBodies
    List<Object> subBodies = node.get_SubBodies();
    if (subBodies.size() != 2) {
      throw new RuntimeException("processASTIfElseNode called with a node without two subBodies");
    }
    // we know there is only two subBodies
    List subBodyOne = (List) subBodies.get(0);
    List subBodyTwo = (List) subBodies.get(1);

    // process Condition
    input = processCondition(node.get_Condition(), input);

    // the current input flowset is sent to both branches
    DavaFlowSet clonedInput = cloneFlowSet(input);

    CPTuple tuple = checkForValueHints(node.get_Condition(), (CPFlowSet) clonedInput, false);
    if (tuple != null) {
      // if not null, is a belief going into the if branch simply add it
      // into the input set
      // System.out.println(">>>>>Adding tuple because of condition into if branch"+tuple.toString());
      ((CPFlowSet) clonedInput).addIfNotPresentButDontUpdate(tuple);
    }

    DavaFlowSet output1 = process(subBodyOne, clonedInput);
    clonedInput = cloneFlowSet(input);
    CPTuple tuple1 = checkForValueHints(node.get_Condition(), (CPFlowSet) clonedInput, true);

    if (tuple1 != null) {
      // if not null, is a belief going into the else branch simply add it
      // into the input set
      // System.out.println(">>>>>Adding tuple because of condition into else branch"+tuple1.toString());
      ((CPFlowSet) clonedInput).addIfNotPresentButDontUpdate(tuple1);
    }
    DavaFlowSet output2 = process(subBodyTwo, clonedInput);

    if (DEBUG_IF) {

      System.out.println(
          "\n\n  IF-ELSE   INPUTS TO MERGE ARE input (if):" + output1.toString() + " else:" + output2.toString() + "\n\n\n");
    }
    DavaFlowSet temp = merge(output1, output2);

    // notice we handle breaks only once since these are breaks to the same
    // label or same node
    String label = getLabel(node);
    output1 = handleBreak(label, temp, node);
    if (DEBUG_IF) {
      System.out.println("Exiting ifelse node" + output1.toString());

    }

    return output1;

  }

  /*
   * The isElseBranch flag is true if the caller is the else branch of the ifelse statement. In that case we might be able to
   * send something for the else branch
   */
  public CPTuple checkForValueHints(ASTCondition cond, CPFlowSet input, boolean isElseBranch) {
    if (cond instanceof ASTUnaryCondition) {
      // check for lone boolean if(notDone)
      ASTUnaryCondition unary = (ASTUnaryCondition) cond;
      Value unaryValue = unary.getValue();

      boolean NOTTED = false;
      // Get the real value if this is a notted expression
      if (unaryValue instanceof DNotExpr) {
        unaryValue = ((DNotExpr) unaryValue).getOp();
        NOTTED = true;
      }

      if (!(unaryValue instanceof Local)) {
        // since we only track locals we cant possibly add info to the
        // inset
        return null;
      }

      // the unary value is a local add the value to the inset which woul
      // dbe present in the if branch
      CPVariable variable = new CPVariable((Local) unaryValue);

      // since NOTTED true means the if branch has variable with value
      // false and vice verse
      if (!isElseBranch) {
        // we are in the if branch hence notted true would mean the
        // variable is actually false here
        Boolean boolVal = new Boolean(!NOTTED);
        return new CPTuple(localClassName, variable, boolVal);
      } else {
        // in the else branch NOTTED true means the variable is true
        Boolean boolVal = new Boolean(NOTTED);
        return new CPTuple(localClassName, variable, boolVal);
      }
    } else if (cond instanceof ASTBinaryCondition) {
      ASTBinaryCondition binary = (ASTBinaryCondition) cond;
      ConditionExpr expr = binary.getConditionExpr();

      Boolean equal = null;

      String symbol = expr.getSymbol();
      if (symbol.indexOf("==") > -1) {
        // System.out.println("!!!!!!!!!1 FOUND == in binary comparison operaiton");
        equal = new Boolean(true);
      } else if (symbol.indexOf("!=") > -1) {
        equal = new Boolean(false);
        // System.out.println("!!!!!!!!!!!!!! FOUND != in binary comparison operaiton");
      } else {
        // a symbol we are not interested in
        // System.out.println("symbol is"+symbol);
        return null;
      }

      // we have a comparison the truth value of equal tells whether we
      // are doing == or !=
      Value a = expr.getOp1();
      Value b = expr.getOp2();

      // see if its possible to deduce a hint from these values
      CPTuple tuple = createCPTupleIfPossible(a, b, input);

      // if the tuple is not created
      if (tuple == null) {
        return null;
      }

      // we have to make sure is that the == and != are taken into account
      if (equal.booleanValue()) {
        // using equality comparison a == b this then means in the if
        // branch a is in fact equal to b
        if (!isElseBranch) {
          return tuple;
        } else {
          return null;
        }
      } else {
        if (isElseBranch) {
          return tuple;
        } else {
          return null;
        }
      }

    }
    return null;
  }

  /*
   * Should create the final tuple to add
   *
   * a == b
   *
   * case 1 a is constant b is constant dont give a damm case 2 a is constant b is a local useful case 3 a is a local b is a
   * constant useful case 4 a is a local or sootfield b is a local or sootfield useful if one of them is in the inset
   */
  public CPTuple createCPTupleIfPossible(Value a, Value b, CPFlowSet input) {
    Object aVal = CPHelper.isAConstantValue(a);
    Object bVal = CPHelper.isAConstantValue(b);

    if (aVal != null && bVal != null) {
      // both are constants dont want to do anything..case 1
      return null;
    }

    CPVariable cpVar = null;
    Object constantToUse = null;

    if (aVal == null && bVal == null) {
      // both are not constants but one of their values could be known in
      // the inset ... case 4
      // System.out.println("a:"+a+" is not a constant b:"+b+" is not");

      // check the input set to see if either a or b have known beliefs.
      // its useful if one and only one of the two has a known belief
      Object av1 = isANotTopConstantInInputSet(input, a);
      Object av2 = isANotTopConstantInInputSet(input, b);
      if (av1 == null && av2 == null) {
        // either top or not present hence useless
        return null;
      } else if (av1 == null && av2 != null) {
        // no value of a found but value of b was found <classname, a,b>
        // System.out.println("From INSET: a:"+a+" is not a constant b "+b+" is"
        // );
        if (!(a instanceof Local && ((Local) a).getType() instanceof PrimType)) {
          // we only hanlde primitive locals
          return null;
        }
        cpVar = new CPVariable((Local) a);
        constantToUse = av2;
      } else if (av1 != null && av2 == null) {
        // no value of b found but value of a was found <classname, b,a>
        // System.out.println("From INSET: a:"+a+" is a constant b "+b+" is not"
        // );
        if (!(b instanceof Local && ((Local) b).getType() instanceof PrimType)) {
          // we only hanlde primitive locals
          return null;
        }
        cpVar = new CPVariable((Local) b);
        constantToUse = av1;
      }
    } else if (aVal != null && bVal == null) {
      // CASE 2: a is a constant and b is not so we have a chance of
      // entering a tuple <className,b,a> maybe
      // System.out.println("a:"+a+" is a constant b:"+b+" is not");

      if (!(b instanceof Local && ((Local) b).getType() instanceof PrimType)) {
        // we only hanlde primitive locals
        return null;
      }

      // able to create cpVar
      cpVar = new CPVariable((Local) b);
      constantToUse = aVal;
    } else if (aVal == null && bVal != null) {
      // CASE 3: a is not a constant but b is a constant so we have a
      // chance of entering a tuple <className,a,b>
      // System.out.println("a:"+a+" is not a constant b:"+b+" is ");

      if (!(a instanceof Local && ((Local) a).getType() instanceof PrimType)) {
        // we only hanlde primitive locals
        return null;
      }

      // able to create cpVar
      cpVar = new CPVariable((Local) a);
      constantToUse = bVal;

    }

    // if cpVar is not null and constantToUse is not null thats good
    if (cpVar != null && constantToUse != null) {
      // create a CPTuple which contains the belief for cpVar with
      // constantToUse we will for sure have going into the if branch
      // we know cpVar is always a local since we create it only for
      // locals

      // need to see if constant is supposed to be a boolean
      // (isAConstantValue returns an Integer for a boolean)
      if (cpVar.getLocal().getType() instanceof BooleanType) {
        if (!(constantToUse instanceof Integer)) {
          // booleans are represented by Integeres in the
          // isConstantValue method what happened here???
          return null;
        }

        Integer tempValue = (Integer) constantToUse;
        if (tempValue.intValue() == 0) {
          constantToUse = new Boolean(false);
        } else {
          constantToUse = new Boolean(true);
        }
      }

      // ready to create the CPTuple

      return new CPTuple(localClassName, cpVar, constantToUse);
    }
    return null;
  }

  /*
   * TODO some other time
   *
   * public Object processASTSwitchNode(ASTSwitchNode node,Object input){
   *
   * }
   */

}
