Projects >> WALA >>b82d5dba907f80908760843329c5c7d1fe4e71ba

Chunk
Conflicting content
<<<<<<< HEAD
package com.ibm.wala.cast.java.ssa;

import java.util.Collection;
import java.util.Collections;

import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.TypeReference;

/**
 * The CAst source language front end for Java has explicit support for lexically-enclosing objects, rather than compiling them
 * away into extra fields and access-control thwarting accessor methods as is done in bytecode. This instruction represents a read
 * of the object of the given type that lexically encloses its use value.
 * 
 * @author Julian Dolby (dolby@us.ibm.com)
 */
public class EnclosingObjectReference extends SSAInstruction {
  private final TypeReference type;

  private final int lval;

  public EnclosingObjectReference(int iindex, int lval, TypeReference type) {
    super(iindex);
    this.lval = lval;
    this.type = type;
  }

  public boolean hasDef() {
    return true;
  }

  public int getDef() {
    return lval;
  }

  public int getDef(int i) {
    assert i == 0;

    return lval;
  }

  public int getNumberOfDefs() {
    return 1;
  }

  public TypeReference getEnclosingType() {
    return type;
  }

  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    return ((AstJavaInstructionFactory) insts).EnclosingObjectReference(iindex, defs == null ? lval : defs[0], type);
  }

  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, lval) + " = enclosing " + type.getName();
  }

  public void visit(IVisitor v) {
    ((AstJavaInstructionVisitor) v).visitEnclosingObjectReference(this);
  }

  public int hashCode() {
    return lval * type.hashCode();
  }

  public Collection getExceptionTypes() {
    return Collections.emptySet();
  }

  public boolean isFallThrough() {
    return true;
  }

}
=======
package com.ibm.wala.cast.java.ssa;

import java.util.Collection;
import java.util.Collections;

import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.TypeReference;

/**
 * The CAst source language front end for Java has explicit support for lexically-enclosing objects, rather than compiling them
 * away into extra fields and access-control thwarting accessor methods as is done in bytecode. This instruction represents a read
 * of the object of the given type that lexically encloses its use value.
 * 
 * @author Julian Dolby (dolby@us.ibm.com)
 */
public class EnclosingObjectReference extends SSAInstruction {
  private final TypeReference type;

  private final int lval;

  public EnclosingObjectReference(int lval, TypeReference type) {
    this.lval = lval;
    this.type = type;
  }

  public boolean hasDef() {
    return true;
  }

  public int getDef() {
    return lval;
  }

  public int getDef(int i) {
    assert i == 0;

    return lval;
  }

  public int getNumberOfDefs() {
    return 1;
  }

  public TypeReference getEnclosingType() {
    return type;
  }

  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    return ((AstJavaInstructionFactory) insts).EnclosingObjectReference(defs == null ? lval : defs[0], type);
  }

  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, lval) + " = enclosing " + type.getName();
  }

  public void visit(IVisitor v) {
    ((AstJavaInstructionVisitor) v).visitEnclosingObjectReference(this);
  }

  public int hashCode() {
    return lval * type.hashCode();
  }

  public Collection getExceptionTypes() {
    return Collections.emptySet();
  }

  public boolean isFallThrough() {
    return true;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
package com.ibm.wala.cast.java.ssa;

import java.util.Collection;
import java.util.Collections;

import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.TypeReference;

/**
 * The CAst source language front end for Java has explicit support for lexically-enclosing objects, rather than compiling them
 * away into extra fields and access-control thwarting accessor methods as is done in bytecode. This instruction represents a read
 * of the object of the given type that lexically encloses its use value.
 * 
 * @author Julian Dolby (dolby@us.ibm.com)
 */
public class EnclosingObjectReference extends SSAInstruction {
  private final TypeReference type;

  private final int lval;

  public EnclosingObjectReference(int iindex, int lval, TypeReference type) {
    super(iindex);
    this.lval = lval;
    this.type = type;
  }

  public boolean hasDef() {
    return true;
  }

  public int getDef() {
    return lval;
  }

  public int getDef(int i) {
    assert i == 0;

    return lval;
  }

  public int getNumberOfDefs() {
    return 1;
  }

  public TypeReference getEnclosingType() {
    return type;
  }

  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    return ((AstJavaInstructionFactory) insts).EnclosingObjectReference(iindex, defs == null ? lval : defs[0], type);
  }

  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, lval) + " = enclosing " + type.getName();
  }

  public void visit(IVisitor v) {
    ((AstJavaInstructionVisitor) v).visitEnclosingObjectReference(this);
  }

  public int hashCode() {
    return lval * type.hashCode();
  }

  public Collection getExceptionTypes() {
    return Collections.emptySet();
  }

  public boolean isFallThrough() {
    return true;
  }

}
File
EnclosingObjectReference.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
import com.ibm.wala.ipa.callgraph.CallGraphBuilderCancelException;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.cha.ClassHierarchyException;
<<<<<<< HEAD
import com.ibm.wala.util.collections.Iterator2Iterable;
=======
import com.ibm.wala.util.NullProgressMonitor;
import com.ibm.wala.util.ProgressMaster;
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
import com.ibm.wala.util.io.CommandLine;
import com.ibm.wala.util.io.FileProvider;
Solution content
import com.ibm.wala.ipa.callgraph.CallGraphBuilderCancelException;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.cha.ClassHierarchyException;
import com.ibm.wala.util.NullProgressMonitor;
import com.ibm.wala.util.ProgressMaster;
import com.ibm.wala.util.io.CommandLine;
import com.ibm.wala.util.io.FileProvider;
import com.ibm.wala.util.io.FileUtil;
File
HTMLCGBuilder.java
Developer's decision
Manual
Kind of conflict
Import
Chunk
Conflicting content
    JSCallGraphUtil.dumpCG(B.getPointerAnalysis(), CG);
    verifyGraphAssertions(CG, assertionsForDispatch);
  }
<<<<<<< HEAD
=======

  private static final Object[][] assertionsForDispatchSameTarget = new Object[][] {
    new Object[] { ROOT, new String[] { "tests/dispatch_same_target.js" } },
    new Object[] { "tests/dispatch_same_target.js/f3", new String[] { "tests/dispatch_same_target.js/f4" } } 
  };


  @Test
  public void testDispatchSameTarget() throws IOException, IllegalArgumentException, CancelException {
    PropagationCallGraphBuilder B = JSCallGraphBuilderUtil.makeScriptCGBuilder("tests", "dispatch_same_target.js");
    CallGraph CG = B.makeCallGraph(B.getOptions());
//    JSCallGraphUtil.AVOID_DUMP = false;
//    JSCallGraphUtil.dumpCG(B.getPointerAnalysis(), CG);
    verifyGraphAssertions(CG, assertionsForDispatchSameTarget);
  }
  
  
  private static final Object[][] assertionsForForInPrototype = new Object[][] {
    new Object[] { ROOT, new String[] { "tests/for_in_prototype.js" } },
    new Object[] { "tests/for_in_prototype.js", new String[] { "suffix:A",
                                                               "suffix:reachable",
                                                               "suffix:also_reachable" } }
  };
  
  @Test
  public void testForInPrototype() throws IllegalArgumentException, IOException, CancelException {
    CallGraph cg = JSCallGraphBuilderUtil.makeScriptCG("tests", "for_in_prototype.js");
    verifyGraphAssertions(cg, assertionsForForInPrototype);
  }
  
  private static final Object[][] assertionsForArrayIndexConv = new Object[][] {
    new Object[] { ROOT, new String[] { "tests/array_index_conv.js" } },
    new Object[] { "tests/array_index_conv.js", new String[] { "suffix:reachable",
                                                               "suffix:also_reachable",
                                                               "suffix:reachable_too" } }
  };
  
  @Test
  public void testArrayIndexConv() throws IllegalArgumentException, IOException, CancelException {
    PropagationCallGraphBuilder b = JSCallGraphBuilderUtil.makeScriptCGBuilder("tests", "array_index_conv.js");
    CallGraph cg = b.makeCallGraph(b.getOptions());
    verifyGraphAssertions(cg, assertionsForArrayIndexConv);
  }

  private static final Object[][] assertionsForArrayIndexConv2 = new Object[][] {
    new Object[] { ROOT, new String[] { "tests/array_index_conv2.js" } },
    new Object[] { "tests/array_index_conv2.js", new String[] { "suffix:invokeOnA" } },
    new Object[] { "suffix:invokeOnA", new String[] { "suffix:reachable",
                                                      "suffix:also_reachable",
                                                      "suffix:reachable_too" } }
  };
  
  @Test
  public void testArrayIndexConv2() throws IllegalArgumentException, IOException, CancelException {
    PropagationCallGraphBuilder b = JSCallGraphBuilderUtil.makeScriptCGBuilder("tests", "array_index_conv2.js");
    b.setContextSelector(new ForInContextSelector(b.getContextSelector()));
    CallGraph cg = b.makeCallGraph(b.getOptions());
    verifyGraphAssertions(cg, assertionsForArrayIndexConv2);
  }
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6

  protected IVector>> computeIkIdToVns(PointerAnalysis pa) {
Solution content
    JSCallGraphUtil.dumpCG(B.getPointerAnalysis(), CG);
    verifyGraphAssertions(CG, assertionsForDispatch);
  }

  private static final Object[][] assertionsForDispatchSameTarget = new Object[][] {
    new Object[] { ROOT, new String[] { "tests/dispatch_same_target.js" } },
  
    new Object[] { "tests/dispatch_same_target.js/f3", new String[] { "tests/dispatch_same_target.js/f4" } } 
  };


  @Test
  public void testDispatchSameTarget() throws IOException, IllegalArgumentException, CancelException {
    PropagationCallGraphBuilder B = JSCallGraphBuilderUtil.makeScriptCGBuilder("tests", "dispatch_same_target.js");
    CallGraph CG = B.makeCallGraph(B.getOptions());
//    JSCallGraphUtil.AVOID_DUMP = false;
//    JSCallGraphUtil.dumpCG(B.getPointerAnalysis(), CG);
    verifyGraphAssertions(CG, assertionsForDispatchSameTarget);
  }
  
  private static final Object[][] assertionsForForInPrototype = new Object[][] {
    new Object[] { ROOT, new String[] { "tests/for_in_prototype.js" } },
    new Object[] { "tests/for_in_prototype.js", new String[] { "suffix:A",
                                                               "suffix:reachable",
                                                               "suffix:also_reachable" } }
  };
  
  @Test
  public void testForInPrototype() throws IllegalArgumentException, IOException, CancelException {
    CallGraph cg = JSCallGraphBuilderUtil.makeScriptCG("tests", "for_in_prototype.js");
    verifyGraphAssertions(cg, assertionsForForInPrototype);
  }
  
  private static final Object[][] assertionsForArrayIndexConv = new Object[][] {
    new Object[] { ROOT, new String[] { "tests/array_index_conv.js" } },
    new Object[] { "tests/array_index_conv.js", new String[] { "suffix:reachable",
                                                               "suffix:also_reachable",
                                                               "suffix:reachable_too" } }
  };
  
  @Test
  public void testArrayIndexConv() throws IllegalArgumentException, IOException, CancelException {
    PropagationCallGraphBuilder b = JSCallGraphBuilderUtil.makeScriptCGBuilder("tests", "array_index_conv.js");
    CallGraph cg = b.makeCallGraph(b.getOptions());
    verifyGraphAssertions(cg, assertionsForArrayIndexConv);
  }

  private static final Object[][] assertionsForArrayIndexConv2 = new Object[][] {
    new Object[] { ROOT, new String[] { "tests/array_index_conv2.js" } },
    new Object[] { "tests/array_index_conv2.js", new String[] { "suffix:invokeOnA" } },
    new Object[] { "suffix:invokeOnA", new String[] { "suffix:reachable",
                                                      "suffix:also_reachable",
                                                      "suffix:reachable_too" } }
  };
  
  @Test
  public void testArrayIndexConv2() throws IllegalArgumentException, IOException, CancelException {
    PropagationCallGraphBuilder b = JSCallGraphBuilderUtil.makeScriptCGBuilder("tests", "array_index_conv2.js");
    b.setContextSelector(new ForInContextSelector(b.getContextSelector()));
    CallGraph cg = b.makeCallGraph(b.getOptions());
    verifyGraphAssertions(cg, assertionsForArrayIndexConv2);
  }

  protected IVector>> computeIkIdToVns(PointerAnalysis pa) {
File
TestSimpleCallGraphShape.java
Developer's decision
Version 2
Kind of conflict
Annotation
Attribute
Method declaration
Method invocation
Chunk
Conflicting content
      S.addConstant(constVN, new ConstantValue(i-1));
      int propertyReadResult = curValNum++;
      // 4 is position of arguments array
<<<<<<< HEAD
      S.addStatement(insts.PropertyRead(S.getNextProgramCounter(), propertyReadResult, 4, constVN));
=======
      S.addStatement(insts.PropertyRead(propertyReadResult, 4, constVN));
      S.getNextProgramCounter();
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
      paramsToPassToInvoked[i] = propertyReadResult;
    }
    return curValNum;
Solution content
      S.addConstant(constVN, new ConstantValue(i-1));
      int propertyReadResult = curValNum++;
      // 4 is position of arguments array
      S.addStatement(insts.PropertyRead(S.getNextProgramCounter(), propertyReadResult, 4, constVN));
      paramsToPassToInvoked[i] = propertyReadResult;
    }
    return curValNum;
File
JavaScriptFunctionApplyContextInterpreter.java
Developer's decision
Version 1
Kind of conflict
Method invocation
Chunk
Conflicting content
  private boolean addCorrelation(CAstEntity entity, Correlation corr, CorrelationSummary correlations) {
    Position startPos = corr.getStartPosition(correlations.getPositions()),
             endPos = corr.getEndPosition(correlations.getPositions());
<<<<<<< HEAD
=======
    
    // TODO: enable these assertions; currently we're getting getLastLine() == -1 a lot
    assert startPos.getFirstLine() != -1;
    //assert startPos.getLastLine() != -1;
    assert endPos.getFirstLine() != -1;
    //assert endPos.getLastLine() != -1;
    
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
    Set startNodes = null,
                  endNodes = null;
    if(!entity.getPosition().getURL().equals(startPos.getURL()))
Solution content
  private boolean addCorrelation(CAstEntity entity, Correlation corr, CorrelationSummary correlations) {
    Position startPos = corr.getStartPosition(correlations.getPositions()),
             endPos = corr.getEndPosition(correlations.getPositions());
    
    // TODO: enable these assertions; currently we're getting getLastLine() == -1 a lot
    assert startPos.getFirstLine() != -1;
    //assert startPos.getLastLine() != -1;
    assert endPos.getFirstLine() != -1;
    //assert endPos.getLastLine() != -1;
    
    Set startNodes = null,
                  endNodes = null;
    if(!entity.getPosition().getURL().equals(startPos.getURL()))
File
CorrelatedPairExtractionPolicy.java
Developer's decision
Version 2
Kind of conflict
Assert statement
Comment
Chunk
Conflicting content
        }
      }
    }
      } else {

<<<<<<< HEAD
/******************************************************************************
 * Copyright (c) 2002 - 2006 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.cast.ipa.callgraph;

          done = true;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Set;

import com.ibm.wala.cast.ir.cfg.AstInducedCFG;
import com.ibm.wala.cast.ir.ssa.AstLexicalRead;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.impl.FakeRootMethod;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.functions.Function;

public class AstCallGraph extends ExplicitCallGraph {
  public AstCallGraph(IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
    super(cha, options, cache);
  }

  public static class AstFakeRoot extends AbstractRootMethod {

    public AstFakeRoot(MethodReference rootMethod, IClass declaringClass, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, declaringClass, cha, options, cache);
    }

    }
    
    public AstFakeRoot(MethodReference rootMethod, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, cha, options, cache);
    }

    public InducedCFG makeControlFlowGraph(SSAInstruction[] statements) {
      return new AstInducedCFG(statements, this, Everywhere.EVERYWHERE);
    }

    public AstLexicalRead addGlobalRead(TypeReference type, String name) {
      AstLexicalRead s = new AstLexicalRead(statements.size(), nextLocal++, null, name);
      statements.add(s);
      return s;
    }
  }

  public static abstract class ScriptFakeRoot extends AstFakeRoot {

    public ScriptFakeRoot(MethodReference rootMethod, IClass declaringClass, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, declaringClass, cha, options, cache);
    }

    public ScriptFakeRoot(MethodReference rootMethod, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, cha, options, cache);
    }

    public abstract SSAAbstractInvokeInstruction addDirectCall(int functionVn, int[] argVns, CallSiteReference callSite);

  }

  public class AstCGNode extends ExplicitNode {
    private Set> callbacks;

    private boolean lexicalScopingChanges = false;
    
    private IR cachedIR;
    
    private DefUse cachedDU;
    
    private AstCGNode(IMethod method, Context context) {
      super(method, context);
    }

    private void fireCallbacks() {
      if (callbacks != null) {
        boolean done = false;
        while (!done) {
          try {
            for (Iterator> x = callbacks.iterator(); x.hasNext();) {
              x.next().apply(null);
            }
          } catch (ConcurrentModificationException e) {
            done = false;
            continue;
          }
    private boolean hasCallback(Function callback) {
      return callbacks != null && callbacks.contains(callback);
    }

    private boolean hasAllCallbacks(Set> callbacks) {
      return callbacks != null && callbacks.containsAll(callbacks);
    }

    public void addCallback(Function callback) {
      if (!hasCallback(callback)) {
        if (callbacks == null) {
          callbacks = HashSetFactory.make(1);
        }

        callbacks.add(callback);

        for (Iterator ps = getCallGraph().getPredNodes(this); ps.hasNext();) {
          ((AstCGNode) ps.next()).addCallback(callback);
        }
      }
    }

    public void addAllCallbacks(Set> callback) {
      if (!hasAllCallbacks(callbacks)) {
        if (callbacks == null) {
          callbacks = HashSetFactory.make(1);
        }

        callbacks.addAll(callback);

        for (Iterator ps = getCallGraph().getPredNodes(this); ps.hasNext();) {
          ((AstCGNode) ps.next()).addAllCallbacks(callback);
        }
      }
    }

    public void setLexicallyMutatedIR(IR ir) {
      lexicalScopingChanges = true;
      cachedIR = ir;
      cachedDU = null;
    }
    
    public void clearMutatedCache(CallSiteReference cs) {
      targets.remove(cs.getProgramCounter());
    public IR getLexicallyMutatedIR() {
      if (lexicalScopingChanges) {
        return cachedIR;
      } else {
        return null;
      }
    }
    
    public DefUse getLexicallyMutatedDU() {
      if (lexicalScopingChanges) {
        if (cachedDU == null) {
          cachedDU = new DefUse(cachedIR);
        }
        return cachedDU;
      } else {
        return null;
      }
    }

    public boolean addTarget(CallSiteReference site, CGNode node) {
      if (super.addTarget(site, node)) {
        if (((AstCGNode) node).callbacks != null) {
          ((AstCGNode) node).fireCallbacks();
          addAllCallbacks(((AstCGNode) node).callbacks);
        }
        return true;
      } else {
        return false;
      }
    }
  }

  protected ExplicitNode makeNode(IMethod method, Context context) {
    return new AstCGNode(method, context);
  }

  protected CGNode makeFakeRootNode() throws CancelException {
    return findOrCreateNode(new AstFakeRoot(FakeRootMethod.rootMethod, cha, options, getAnalysisCache()), Everywhere.EVERYWHERE);
  }

}
=======
/******************************************************************************
 * Copyright (c) 2002 - 2006 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.cast.ipa.callgraph;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Set;

import com.ibm.wala.cast.ir.cfg.AstInducedCFG;
import com.ibm.wala.cast.ir.ssa.AstLexicalRead;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.impl.FakeRootMethod;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.functions.Function;

public class AstCallGraph extends ExplicitCallGraph {
  public AstCallGraph(IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
    super(cha, options, cache);
  }

  public static class AstFakeRoot extends AbstractRootMethod {

    public AstFakeRoot(MethodReference rootMethod, IClass declaringClass, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, declaringClass, cha, options, cache);
    }

    public AstFakeRoot(MethodReference rootMethod, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, cha, options, cache);
    }

    public InducedCFG makeControlFlowGraph(SSAInstruction[] statements) {
      return new AstInducedCFG(statements, this, Everywhere.EVERYWHERE);
    }

    public AstLexicalRead addGlobalRead(TypeReference type, String name) {
      AstLexicalRead s = new AstLexicalRead(nextLocal++, null, name);
      statements.add(s);
      return s;
    }
  }

  public static abstract class ScriptFakeRoot extends AstFakeRoot {

    public ScriptFakeRoot(MethodReference rootMethod, IClass declaringClass, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, declaringClass, cha, options, cache);
    }

    public ScriptFakeRoot(MethodReference rootMethod, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, cha, options, cache);
    }

    public abstract SSAAbstractInvokeInstruction addDirectCall(int functionVn, int[] argVns, CallSiteReference callSite);

  }

  public class AstCGNode extends ExplicitNode {
    private Set> callbacks;

    private boolean lexicalScopingChanges = false;
    
    private IR cachedIR;
    
    private DefUse cachedDU;
    
    private AstCGNode(IMethod method, Context context) {
      super(method, context);
    }

    private void fireCallbacks() {
      if (callbacks != null) {
        boolean done = false;
        while (!done) {
          try {
            for (Iterator> x = callbacks.iterator(); x.hasNext();) {
              x.next().apply(null);
            }
          } catch (ConcurrentModificationException e) {
            done = false;
            continue;
          }
          done = true;
        }
      }
    }

    private boolean hasCallback(Function callback) {
      return callbacks != null && callbacks.contains(callback);
    }

    private boolean hasAllCallbacks(Set> callbacks) {
      return callbacks != null && callbacks.containsAll(callbacks);
    }

    public void addCallback(Function callback) {
      if (!hasCallback(callback)) {
        if (callbacks == null) {
          callbacks = HashSetFactory.make(1);
        }

        callbacks.add(callback);

        for (Iterator ps = getCallGraph().getPredNodes(this); ps.hasNext();) {
          ((AstCGNode) ps.next()).addCallback(callback);
        }
      }
    }

    public void addAllCallbacks(Set> callback) {
      if (!hasAllCallbacks(callbacks)) {
        if (callbacks == null) {
          callbacks = HashSetFactory.make(1);
        }

        callbacks.addAll(callback);

        for (Iterator ps = getCallGraph().getPredNodes(this); ps.hasNext();) {
          ((AstCGNode) ps.next()).addAllCallbacks(callback);
        }
      }
    }

    public void setLexicallyMutatedIR(IR ir) {
      lexicalScopingChanges = true;
      cachedIR = ir;
      cachedDU = null;
    }
    
    public void clearMutatedCache(CallSiteReference cs) {
      targets.remove(cs.getProgramCounter());
    }
    
    public IR getLexicallyMutatedIR() {
      if (lexicalScopingChanges) {
        return cachedIR;
        return null;
      }
    }
    
    public DefUse getLexicallyMutatedDU() {
      if (lexicalScopingChanges) {
        if (cachedDU == null) {
          cachedDU = new DefUse(cachedIR);
        }
        return cachedDU;
      } else {
        return null;
      }
    }

    public boolean addTarget(CallSiteReference site, CGNode node) {
      if (super.addTarget(site, node)) {
        if (((AstCGNode) node).callbacks != null) {
          ((AstCGNode) node).fireCallbacks();
          addAllCallbacks(((AstCGNode) node).callbacks);
        }
        return true;
      } else {
        return false;
      }
    }
  }

  protected ExplicitNode makeNode(IMethod method, Context context) {
    return new AstCGNode(method, context);
  }

  protected CGNode makeFakeRootNode() throws CancelException {
    return findOrCreateNode(new AstFakeRoot(FakeRootMethod.rootMethod, cha, options, getAnalysisCache()), Everywhere.EVERYWHERE);
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
        while (!done) {
/******************************************************************************
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
          try {
 * 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.cast.ipa.callgraph;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Set;

import com.ibm.wala.cast.ir.cfg.AstInducedCFG;
import com.ibm.wala.cast.ir.ssa.AstLexicalRead;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.impl.FakeRootMethod;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.functions.Function;

public class AstCallGraph extends ExplicitCallGraph {
  public AstCallGraph(IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
    super(cha, options, cache);
  }

  public static class AstFakeRoot extends AbstractRootMethod {

    public AstFakeRoot(MethodReference rootMethod, IClass declaringClass, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, declaringClass, cha, options, cache);
    }

    public AstFakeRoot(MethodReference rootMethod, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, cha, options, cache);
    }

    public InducedCFG makeControlFlowGraph(SSAInstruction[] statements) {
      return new AstInducedCFG(statements, this, Everywhere.EVERYWHERE);
    }

    public AstLexicalRead addGlobalRead(TypeReference type, String name) {
      AstLexicalRead s = new AstLexicalRead(statements.size(), nextLocal++, null, name);
      statements.add(s);
      return s;
    }
  }

  public static abstract class ScriptFakeRoot extends AstFakeRoot {

    public ScriptFakeRoot(MethodReference rootMethod, IClass declaringClass, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, declaringClass, cha, options, cache);
    }

    public ScriptFakeRoot(MethodReference rootMethod, IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
      super(rootMethod, cha, options, cache);
    }

    public abstract SSAAbstractInvokeInstruction addDirectCall(int functionVn, int[] argVns, CallSiteReference callSite);

  }

  public class AstCGNode extends ExplicitNode {
    private Set> callbacks;

    private boolean lexicalScopingChanges = false;
    
    private IR cachedIR;
    
    private DefUse cachedDU;
    
    private AstCGNode(IMethod method, Context context) {
      super(method, context);
    }

    private void fireCallbacks() {
      if (callbacks != null) {
        boolean done = false;
            for (Iterator> x = callbacks.iterator(); x.hasNext();) {
              x.next().apply(null);
            }
          } catch (ConcurrentModificationException e) {
            done = false;
            continue;
          }
          done = true;
        }
      }
    }

    private boolean hasCallback(Function callback) {
      return callbacks != null && callbacks.contains(callback);
    }

    private boolean hasAllCallbacks(Set> callbacks) {
      return callbacks != null && callbacks.containsAll(callbacks);
    }

    public void addCallback(Function callback) {
      if (!hasCallback(callback)) {
        if (callbacks == null) {
          callbacks = HashSetFactory.make(1);
        }

        callbacks.add(callback);

        for (Iterator ps = getCallGraph().getPredNodes(this); ps.hasNext();) {
          ((AstCGNode) ps.next()).addCallback(callback);
        }
      }
    }

    public void addAllCallbacks(Set> callback) {
      if (!hasAllCallbacks(callbacks)) {
        if (callbacks == null) {
          callbacks = HashSetFactory.make(1);
        }

        callbacks.addAll(callback);

        for (Iterator ps = getCallGraph().getPredNodes(this); ps.hasNext();) {
          ((AstCGNode) ps.next()).addAllCallbacks(callback);
        }
      }
    }

    public void setLexicallyMutatedIR(IR ir) {
      lexicalScopingChanges = true;
      cachedIR = ir;
      cachedDU = null;
    }
    
    public void clearMutatedCache(CallSiteReference cs) {
      targets.remove(cs.getProgramCounter());
    }
    
    public IR getLexicallyMutatedIR() {
      if (lexicalScopingChanges) {
        return cachedIR;
      } else {
        return null;
      }
    }
    
    public DefUse getLexicallyMutatedDU() {
      if (lexicalScopingChanges) {
        if (cachedDU == null) {
          cachedDU = new DefUse(cachedIR);
        }
        return cachedDU;
      } else {
        return null;
      }
    }

    public boolean addTarget(CallSiteReference site, CGNode node) {
      if (super.addTarget(site, node)) {
        if (((AstCGNode) node).callbacks != null) {
          ((AstCGNode) node).fireCallbacks();
          addAllCallbacks(((AstCGNode) node).callbacks);
        }
        return true;
      } else {
        return false;
      }
    }
  }

  protected ExplicitNode makeNode(IMethod method, Context context) {
    return new AstCGNode(method, context);
  }

  protected CGNode makeFakeRootNode() throws CancelException {
    return findOrCreateNode(new AstFakeRoot(FakeRootMethod.rootMethod, cha, options, getAnalysisCache()), Everywhere.EVERYWHERE);
  }

}
File
AstCallGraph.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/******************************************************************************
 * Copyright (c) 2002 - 2006 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.cast.ir.ssa;

import com.ibm.wala.cast.ir.ssa.AstLexicalAccess.Access;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.ssa.SymbolTable;

/**
 * This abstract class adds to invoke instructions the ability to handle lexical uses and definitions during call graph
    return def >= super.getNumberOfDefs();
 * construction. The lexical uses and definitions of these objects are initially empty, and get filled in by the
 * AstSSAPropagationCallGraphBuilder, particularly its LexicalOperator objects. This class is still abstract since the
 * lexical scoping functionality is used by multiple languages, each of which has further specializations of invoke
 * instructions.
 * 
 * @author Julian Dolby (dolby@us.ibm.com)
 * 
 */
public abstract class AbstractLexicalInvoke extends MultiReturnValueInvokeInstruction {

  protected Access[] lexicalReads = null;

  protected Access[] lexicalWrites = null;

  protected AbstractLexicalInvoke(int iindex, int results[], int exception, CallSiteReference site) {
    super(iindex, results, exception, site);
  }

  protected AbstractLexicalInvoke(int iindex, int result, int exception, CallSiteReference site) {
    this(iindex, new int[] { result }, exception, site);
  }

  protected AbstractLexicalInvoke(int iindex, int results[], int exception, CallSiteReference site, Access[] lexicalReads,
      Access[] lexicalWrites) {
    this(iindex, results, exception, site);
    this.lexicalReads = lexicalReads;
    this.lexicalWrites = lexicalWrites;
  }

  public int getNumberOfUses() {
    if (lexicalReads == null)
      return getNumberOfParameters();
    else
      return getNumberOfParameters() + lexicalReads.length;
  }
  
  public int getNumberOfLexicalWrites(){
    if(lexicalWrites == null){
      return 0;
    } else {
      return lexicalWrites.length;
    }
  }
  
  public int getNumberOfLexicalReads() {
    if(lexicalReads == null){
      return 0;
    } else {
      return lexicalReads.length;
    }
  }

  public final int getLastLexicalUse() {
    if (lexicalReads == null) {
      return -1;
    } else {
      return getNumberOfParameters() + lexicalReads.length - 1;
    }
  }

  public int getUse(int j) {
    assert j >= getNumberOfParameters();
    assert lexicalReads != null;
    assert lexicalReads[j - getNumberOfParameters()] != null;
    return lexicalReads[j - getNumberOfParameters()].valueNumber;
  }

  public int getNumberOfDefs() {
    if (lexicalWrites == null)
      return super.getNumberOfDefs();
    else
      return super.getNumberOfDefs() + lexicalWrites.length;
  }

  public int getDef(int j) {
    if (j < super.getNumberOfDefs())
      return super.getDef(j);
    else
      return lexicalWrites[j - super.getNumberOfDefs()].valueNumber;
  }

  private Access[] addAccess(Access[] array, Access access) {
    if (array == null)
      return new Access[] { access };
    else {
      Access[] result = new Access[array.length + 1];
      System.arraycopy(array, 0, result, 0, array.length);
      result[array.length] = access;
      return result;
    }
  }

  public boolean isLexicalUse(int use) {
    return use >= getNumberOfParameters();
  }

  public void addLexicalUse(Access use) {
    lexicalReads = addAccess(lexicalReads, use);
  }

  public Access getLexicalUse(int i) {
    return lexicalReads[i - getNumberOfParameters()];
  }

  public boolean isLexicalDef(int def) {
  }

  public void addLexicalDef(Access def) {
    lexicalWrites = addAccess(lexicalWrites, def);
  }

  public Access getLexicalDef(int i) {
    return lexicalWrites[i - super.getNumberOfDefs()];
  }

  public int hashCode() {
    return site.hashCode() * 7529;
  }

  public String toString(SymbolTable symbolTable) {
    StringBuffer s = new StringBuffer(super.toString(symbolTable));

    if (lexicalReads != null) {
      s.append(" (reads:");
      for (int i = 0; i < lexicalReads.length; i++) {
        s.append(" ").append(lexicalReads[i].variableName).append(":").append(
            getValueString(symbolTable, lexicalReads[i].valueNumber));
      }
      s.append(")");
    }

    if (lexicalWrites != null) {
      s.append(" (writes:");
      for (int i = 0; i < lexicalWrites.length; i++) {
        s.append(" ").append(lexicalWrites[i].variableName).append(":").append(
            getValueString(symbolTable, lexicalWrites[i].valueNumber));
      }
      s.append(")");
    }

    return s.toString();
  }
}
=======
/******************************************************************************
 * Copyright (c) 2002 - 2006 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.cast.ir.ssa;

import com.ibm.wala.cast.ir.ssa.AstLexicalAccess.Access;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.ssa.SymbolTable;

/**
 * This abstract class adds to invoke instructions the ability to handle lexical uses and definitions during call graph
 * construction. The lexical uses and definitions of these objects are initially empty, and get filled in by the
 * AstSSAPropagationCallGraphBuilder, particularly its LexicalOperator objects. This class is still abstract since the
 * lexical scoping functionality is used by multiple languages, each of which has further specializations of invoke
 * instructions.
 * 
 * @author Julian Dolby (dolby@us.ibm.com)
 * 
 */
public abstract class AbstractLexicalInvoke extends MultiReturnValueInvokeInstruction {

  protected Access[] lexicalReads = null;

  protected Access[] lexicalWrites = null;

  protected AbstractLexicalInvoke(int results[], int exception, CallSiteReference site) {
    super(results, exception, site);
  }

  protected AbstractLexicalInvoke(int result, int exception, CallSiteReference site) {
    this(new int[] { result }, exception, site);
  }

  protected AbstractLexicalInvoke(int results[], int exception, CallSiteReference site, Access[] lexicalReads,
      Access[] lexicalWrites) {
    this(results, exception, site);
    this.lexicalReads = lexicalReads;
    this.lexicalWrites = lexicalWrites;
  }

  public int getNumberOfUses() {
    if (lexicalReads == null)
      return getNumberOfParameters();
    else
      return getNumberOfParameters() + lexicalReads.length;
  }
  
  public int getNumberOfLexicalWrites(){
    if(lexicalWrites == null){
      return 0;
    } else {
      return lexicalWrites.length;
    }
  }
  
  public int getNumberOfLexicalReads() {
    if(lexicalReads == null){
      return 0;
    } else {
      return lexicalReads.length;
    }
  }

  public final int getLastLexicalUse() {
    if (lexicalReads == null) {
      return -1;
    } else {
      return getNumberOfParameters() + lexicalReads.length - 1;
    }
  }

  public int getUse(int j) {
    assert j >= getNumberOfParameters();
    assert lexicalReads != null;
    assert lexicalReads[j - getNumberOfParameters()] != null;
    return lexicalReads[j - getNumberOfParameters()].valueNumber;
  }

  public int getNumberOfDefs() {
    if (lexicalWrites == null)
      return super.getNumberOfDefs();
    else
      return super.getNumberOfDefs() + lexicalWrites.length;
  }

  public int getDef(int j) {
    if (j < super.getNumberOfDefs())
      return super.getDef(j);
    else
      return lexicalWrites[j - super.getNumberOfDefs()].valueNumber;
  }

  private Access[] addAccess(Access[] array, Access access) {
    if (array == null)
      return new Access[] { access };
    else {
      Access[] result = new Access[array.length + 1];
      System.arraycopy(array, 0, result, 0, array.length);
      result[array.length] = access;
      return result;
    }

  public boolean isLexicalUse(int use) {
    return use >= getNumberOfParameters();
  }

  public void addLexicalUse(Access use) {
    lexicalReads = addAccess(lexicalReads, use);
  }

  public Access getLexicalUse(int i) {
    return lexicalReads[i - getNumberOfParameters()];
  }

  public boolean isLexicalDef(int def) {
    return def >= super.getNumberOfDefs();
  }

  public void addLexicalDef(Access def) {
    lexicalWrites = addAccess(lexicalWrites, def);
  }

  public Access getLexicalDef(int i) {
    return lexicalWrites[i - super.getNumberOfDefs()];
  }

  public int hashCode() {
    return site.hashCode() * 7529;
  }

  public String toString(SymbolTable symbolTable) {
    StringBuffer s = new StringBuffer(super.toString(symbolTable));

    if (lexicalReads != null) {
      s.append(" (reads:");
      for (int i = 0; i < lexicalReads.length; i++) {
        s.append(" ").append(lexicalReads[i].variableName).append(":").append(
            getValueString(symbolTable, lexicalReads[i].valueNumber));
      }
      s.append(")");
    }

    if (lexicalWrites != null) {
      s.append(" (writes:");
      for (int i = 0; i < lexicalWrites.length; i++) {
        s.append(" ").append(lexicalWrites[i].variableName).append(":").append(
            getValueString(symbolTable, lexicalWrites[i].valueNumber));
      }
      s.append(")");
    }

    return s.toString();
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/******************************************************************************
 * Copyright (c) 2002 - 2006 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.cast.ir.ssa;

import com.ibm.wala.cast.ir.ssa.AstLexicalAccess.Access;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.ssa.SymbolTable;

/**
 * This abstract class adds to invoke instructions the ability to handle lexical uses and definitions during call graph
 * construction. The lexical uses and definitions of these objects are initially empty, and get filled in by the
 * AstSSAPropagationCallGraphBuilder, particularly its LexicalOperator objects. This class is still abstract since the
 * lexical scoping functionality is used by multiple languages, each of which has further specializations of invoke
 * instructions.
 * 
 * @author Julian Dolby (dolby@us.ibm.com)
 * 
 */
public abstract class AbstractLexicalInvoke extends MultiReturnValueInvokeInstruction {

  protected Access[] lexicalReads = null;

  protected Access[] lexicalWrites = null;

  protected AbstractLexicalInvoke(int iindex, int results[], int exception, CallSiteReference site) {
    super(iindex, results, exception, site);
  }

  protected AbstractLexicalInvoke(int iindex, int result, int exception, CallSiteReference site) {
    this(iindex, new int[] { result }, exception, site);
  }

  protected AbstractLexicalInvoke(int iindex, int results[], int exception, CallSiteReference site, Access[] lexicalReads,
      Access[] lexicalWrites) {
    this(iindex, results, exception, site);
    this.lexicalReads = lexicalReads;
    this.lexicalWrites = lexicalWrites;
  }

  public int getNumberOfUses() {
    if (lexicalReads == null)
      return getNumberOfParameters();
    else
      return getNumberOfParameters() + lexicalReads.length;
  }
  
  public int getNumberOfLexicalWrites(){
    if(lexicalWrites == null){
      return 0;
    } else {
      return lexicalWrites.length;
    }
  }
  
  public int getNumberOfLexicalReads() {
    if(lexicalReads == null){
      return 0;
    } else {
      return lexicalReads.length;
    }
  }

  public final int getLastLexicalUse() {
    if (lexicalReads == null) {
      return -1;
    } else {
      return getNumberOfParameters() + lexicalReads.length - 1;
    }
  }

  public int getUse(int j) {
    assert j >= getNumberOfParameters();
    assert lexicalReads != null;
    assert lexicalReads[j - getNumberOfParameters()] != null;
    return lexicalReads[j - getNumberOfParameters()].valueNumber;
  }

  public int getNumberOfDefs() {
    if (lexicalWrites == null)
      return super.getNumberOfDefs();
    else
      return super.getNumberOfDefs() + lexicalWrites.length;
  }

  public int getDef(int j) {
    if (j < super.getNumberOfDefs())
      return super.getDef(j);
    else
      return lexicalWrites[j - super.getNumberOfDefs()].valueNumber;
  }

  private Access[] addAccess(Access[] array, Access access) {
    if (array == null)
      return new Access[] { access };
    else {
      Access[] result = new Access[array.length + 1];
      System.arraycopy(array, 0, result, 0, array.length);
      result[array.length] = access;
      return result;
    }
  }

  public boolean isLexicalUse(int use) {
    return use >= getNumberOfParameters();
  }

  public void addLexicalUse(Access use) {
    lexicalReads = addAccess(lexicalReads, use);
  }

  public Access getLexicalUse(int i) {
    return lexicalReads[i - getNumberOfParameters()];
  }

  public boolean isLexicalDef(int def) {
    return def >= super.getNumberOfDefs();
  }

  public void addLexicalDef(Access def) {
    lexicalWrites = addAccess(lexicalWrites, def);
  }

  public Access getLexicalDef(int i) {
    return lexicalWrites[i - super.getNumberOfDefs()];
  }

  public int hashCode() {
    return site.hashCode() * 7529;
  }

  public String toString(SymbolTable symbolTable) {
    StringBuffer s = new StringBuffer(super.toString(symbolTable));

    if (lexicalReads != null) {
      s.append(" (reads:");
      for (int i = 0; i < lexicalReads.length; i++) {
        s.append(" ").append(lexicalReads[i].variableName).append(":").append(
            getValueString(symbolTable, lexicalReads[i].valueNumber));
      }
      s.append(")");
    }

    if (lexicalWrites != null) {
      s.append(" (writes:");
      for (int i = 0; i < lexicalWrites.length; i++) {
        s.append(" ").append(lexicalWrites[i].variableName).append(":").append(
            getValueString(symbolTable, lexicalWrites[i].valueNumber));
      }
      s.append(")");
    }

    return s.toString();
  }
}
File
AbstractLexicalInvoke.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
   */
  private final int lval;

<<<<<<< HEAD
  public AstIsDefinedInstruction(int iindex, int lval, int rval, int fieldVal, FieldReference fieldRef) {
    super(iindex);
=======
  /**
   * This constructor should only be used from {@link SSAInstruction#copyForSSA(SSAInstructionFactory, int[], int[])}
   */
  public AstIsDefinedInstruction(int lval, int rval, int fieldVal, FieldReference fieldRef) {
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
    this.lval = lval;
    this.rval = rval;
    this.fieldVal = fieldVal;
Solution content
   */
  private final int lval;

  /**
   * This constructor should only be used from {@link SSAInstruction#copyForSSA(SSAInstructionFactory, int[], int[])}
   */
  public AstIsDefinedInstruction(int iindex, int lval, int rval, int fieldVal, FieldReference fieldRef) {
    super(iindex);
    this.lval = lval;
    this.rval = rval;
    this.fieldVal = fieldVal;
File
AstIsDefinedInstruction.java
Developer's decision
Combination
Kind of conflict
Comment
Method invocation
Method signature
Chunk
Conflicting content
<<<<<<< HEAD
/******************************************************************************
 * Copyright (c) 2002 - 2006 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.cast.ir.translator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.cast.ir.ssa.AssignInstruction;
import com.ibm.wala.cast.ir.ssa.AstAssertInstruction;
import com.ibm.wala.cast.ir.ssa.AstConstants;
import com.ibm.wala.cast.ir.ssa.AstEchoInstruction;
import com.ibm.wala.cast.ir.ssa.AstGlobalRead;
import com.ibm.wala.cast.ir.ssa.AstGlobalWrite;
import com.ibm.wala.cast.ir.ssa.AstIsDefinedInstruction;
import com.ibm.wala.cast.ir.ssa.AstLexicalAccess;
import com.ibm.wala.cast.ir.ssa.AstLexicalAccess.Access;
import com.ibm.wala.cast.ir.ssa.AstLexicalRead;
import com.ibm.wala.cast.ir.ssa.AstLexicalWrite;
import com.ibm.wala.cast.ir.ssa.EachElementGetInstruction;
import com.ibm.wala.cast.ir.ssa.EachElementHasNextInstruction;
import com.ibm.wala.cast.ir.ssa.SSAConversion;
import com.ibm.wala.cast.loader.AstMethod.DebuggingInformation;
import com.ibm.wala.cast.loader.AstMethod.LexicalInformation;
import com.ibm.wala.cast.loader.CAstAbstractLoader;
import com.ibm.wala.cast.tree.CAstControlFlowMap;
import com.ibm.wala.cast.tree.CAstEntity;
import com.ibm.wala.cast.tree.CAstNode;
import com.ibm.wala.cast.tree.CAstSourcePositionMap;
import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
import com.ibm.wala.cast.tree.CAstSymbol;
import com.ibm.wala.cast.tree.CAstType;
import com.ibm.wala.cast.tree.impl.CAstCloner;
import com.ibm.wala.cast.tree.impl.CAstImpl;
import com.ibm.wala.cast.tree.impl.CAstOperator;
import com.ibm.wala.cast.tree.impl.CAstRewriter;
import com.ibm.wala.cast.tree.impl.CAstSymbolImpl;
import com.ibm.wala.cast.tree.impl.CAstSymbolImplBase;
import com.ibm.wala.cast.tree.visit.CAstVisitor;
import com.ibm.wala.cast.types.AstTypeReference;
import com.ibm.wala.cast.util.CAstPrinter;
import com.ibm.wala.cfg.AbstractCFG;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.IClassLoader;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.ModuleEntry;
import com.ibm.wala.shrikeBT.BinaryOpInstruction;
import com.ibm.wala.shrikeBT.ConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.ShiftInstruction;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAMonitorInstruction;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.INodeWithNumber;
import com.ibm.wala.util.graph.impl.SparseNumberedGraph;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.warnings.Warning;

/**
 * Common code to translate CAst to IR. Must be specialized by each language to
 * handle semantics appropriately.
 */
public abstract class AstTranslator extends CAstVisitor implements ArrayOpHandler, TranslatorToIR {

  /**
   * set to true to use new handling of lexical scoping
   */
  public static final boolean NEW_LEXICAL = true;


  /**
   * does the language care about using type-appropriate default values? For
   * Java, the answer is yes (ints should get a default value of 0, null for
   * pointers, etc.). For JavaScript, the answer is no, as any variable can hold
   * the value 'undefined'.
   */
  protected abstract boolean useDefaultInitValues();

  /**
   * can lexical reads / writes access globals?
   */
  protected abstract boolean treatGlobalsAsLexicallyScoped();

  /**
   * given accesses in a method to variables defined in an enclosing lexical
   * scope, is it legal to read the variable into a local l once at the
   * beginning of the method, operate on l through the method body (rather than
   * performing separate lexical read / write operations), and write back the
   * value in l (if necessary) at the end of the method?
   */
  protected abstract boolean useLocalValuesForLexicalVars();

  protected boolean topLevelFunctionsInGlobalScope() {
    return true;
  }

  /**
   * for a block that catches all exceptions, what is the root exception type
   * that it can catch? E.g., for Java, java.lang.Throwable
   */
  protected abstract TypeReference defaultCatchType();

  protected abstract TypeReference makeType(CAstType type);

  /**
   * define a new (presumably nested) type. return true if type was successfully
   * defined, false otherwise
   */
  protected abstract boolean defineType(CAstEntity type, WalkContext wc);

  /**
   * declare a new function, represented by N
   */
  protected abstract void declareFunction(CAstEntity N, WalkContext context);

  /**
   * fully define a function. invoked after all the code of the function has
   * been processed
   */
  protected abstract void defineFunction(CAstEntity N, WalkContext definingContext, AbstractCFG cfg, SymbolTable symtab,
      boolean hasCatchBlock, TypeReference[][] caughtTypes, boolean hasMonitorOp, AstLexicalInformation lexicalInfo,
      DebuggingInformation debugInfo);

  /**
   * define a new field fieldEntity within topEntity
   */
  protected abstract void defineField(CAstEntity topEntity, WalkContext context, CAstEntity fieldEntity);

  /**
   * create the language-appropriate name for f
   */
  protected abstract String composeEntityName(WalkContext parent, CAstEntity f);

  /**
   * generate IR for a CAst throw expression, updating context.cfg()
   */
  protected abstract void doThrow(WalkContext context, int exception);

  /**
   * generate IR for a CAst array read, updating context.cfg()
   */
  public abstract void doArrayRead(WalkContext context, int result, int arrayValue, CAstNode arrayRef, int[] dimValues);

  /**
   * generate IR for a CAst array write, updating context.cfg()
   */
  public abstract void doArrayWrite(WalkContext context, int arrayValue, CAstNode arrayRef, int[] dimValues, int rval);

  /**
   * generate IR for a CAst field read, updating context.cfg()
   */
  protected abstract void doFieldRead(WalkContext context, int result, int receiver, CAstNode elt, CAstNode parent);

  /**
   * generate IR for a CAst field write, updating context.cfg()
   */
  protected abstract void doFieldWrite(WalkContext context, int receiver, CAstNode elt, CAstNode parent, int rval);

  /**
   * generate IR for a CAst function expression, updating context.cfg()
   */
  protected abstract void doMaterializeFunction(CAstNode node, WalkContext context, int result, int exception, CAstEntity fn);

  /**
   * generate IR for a CAst new expression, updating context.cfg()
   */
  protected abstract void doNewObject(WalkContext context, CAstNode newNode, int result, Object type, int[] arguments);

  /**
   * generate IR for a CAst method call expression, updating context.cfg()
   */
  protected abstract void doCall(WalkContext context, CAstNode call, int result, int exception, CAstNode name, int receiver,
      int[] arguments);

  /**
   * used to generate instructions for array operations; defaults to this
   */
  private ArrayOpHandler arrayOpHandler;

  protected boolean isExceptionLabel(Object label) {
    if (label == null)
      return false;
    if (label instanceof Boolean)
      return false;
    if (label instanceof Number)
      return false;
    if (label == CAstControlFlowMap.SWITCH_DEFAULT)
      return false;
    return true;
  }

  /**
   * If this returns true, new global declarations get created for any attempt
   * to access a non-existent variable (believe it or not, JavaScript actually
   * does this!)
   */
  protected boolean hasImplicitGlobals() {
    return false;
  }

  /**
   * If this returns true, then attempts to lookup non-existent names return
   * `null' rather than tripping an assertion. This can be used when special
   * handling is needed for built-in names. (PHP does this)
   */
  protected boolean hasSpecialUndeclaredVariables() {
    return false;
  }

  /**
   * some languages let you omit initialization of certain fields when writing
   * an object literal (e.g., PHP). This method should be overridden to handle
   * such cases.
   */
  protected void handleUnspecifiedLiteralKey(WalkContext context, CAstNode objectLiteralNode, int unspecifiedLiteralIndex,
      CAstVisitor visitor) {
    Assertions.UNREACHABLE();
  }

  /**
   * generate prologue code for each function body
   */
  protected void doPrologue(WalkContext context) {
    // if we are SSA converting lexical accesses, add a placeholder instruction
    // eventually (via mutation of its Access array) reads all relevant lexical
    // variables at the beginning of the method.
    if (useLocalValuesForLexicalVars()) {
      context.cfg().addInstruction(new AstLexicalRead(context.cfg().currentInstruction, new Access[0]));
    } else {
      // perform a lexical write to copy the value stored in the local
      // associated with each parameter to the lexical name
      final CAstEntity entity = context.top();
      Set exposedNames = entity2ExposedNames.get(entity);
      if (exposedNames != null) {
        for (String arg : entity.getArgumentNames()) {
          if (exposedNames.contains(arg)) {
            final Scope currentScope = context.currentScope();
            Symbol symbol = currentScope.lookup(arg);
            assert symbol.getDefiningScope() == currentScope;
            int argVN = symbol.valueNumber();
            Access A = new Access(arg, context.getEntityName(entity), argVN);
            context.cfg().addInstruction(new AstLexicalWrite(context.cfg().currentInstruction, A));
          }
        }
      }
    }
  }

  /**
   * generate IR for call modeling creation of primitive value, updating
   * context.cfg()
   */
  protected abstract void doPrimitive(int resultVal, WalkContext context, CAstNode primitiveCall);

  /**
   * get the value number for a name defined locally (i.e., within the current
   * method) by looking up the name in context.currentScope(). Note that the
   * caller is responsible for ensuring that name is defined in the local scope.
   */
  protected int doLocalRead(WalkContext context, String name) {
    if (!useLocalValuesForLexicalVars()) {
      CAstEntity entity = context.top();
      Set exposed = entity2ExposedNames.get(entity);
      if (exposed != null && exposed.contains(name)) {
        return doLexReadHelper(context, name);
      }
    }
    return context.currentScope().lookup(name).valueNumber();
  }

  /**
   * add an {@link AssignInstruction} to context.cfg() that copies rval to the
   * value number of local nm. Note that the caller is responsible for ensuring
   * that nm is defined in the local scope.
   */
  protected void doLocalWrite(WalkContext context, String nm, int rval) {
    if (!useLocalValuesForLexicalVars()) {
      CAstEntity entity = context.top();
      Set exposed = entity2ExposedNames.get(entity);
      if (exposed != null && exposed.contains(nm)) {
        // use a lexical write
        doLexicallyScopedWrite(context, nm, rval);
        return;
      }
    }
    int lval = context.currentScope().lookup(nm).valueNumber();
    if (lval != rval) {
      context.cfg().addInstruction(new AssignInstruction(context.cfg().currentInstruction, lval, rval));
    }
  }

  /**
   * Note that the caller is responsible for ensuring that name is defined in a
   * lexical scope.
   * 
   * @param node
   *          the AST node representing the read
   * @param context
   * @param name
   * @return
   */
  protected int doLexicallyScopedRead(CAstNode node, WalkContext context, final String name) {
    return doLexReadHelper(context, name);
  }

  /**
   * we only have this method to avoid having to pass a node parameter at other
   * call sites, as would be required for
   * {@link #doLexicallyScopedRead(CAstNode, WalkContext, String)}
   */
  private int doLexReadHelper(WalkContext context, final String name) {
    Symbol S = context.currentScope().lookup(name);
    Scope definingScope = S.getDefiningScope();
    CAstEntity E = definingScope.getEntity();
    // record in declaring scope that the name is exposed to a nested scope
//<<<<<<< .mine
//    Symbol S = context.currentScope().lookup(name);
//    CAstEntity E = S.getDefiningScope().getEntity();
//    addExposedName(E, E, name, S.getDefiningScope().lookup(name).valueNumber(), false, context);
//=======
    addExposedName(E, E, name, definingScope.lookup(name).valueNumber(), false, context);
//>>>>>>> .r4421

    final String entityName = context.getEntityName(E);
    if (useLocalValuesForLexicalVars()) {
      // lexically-scoped variables can be given a single vn in a method
//<<<<<<< .mine
//      Access A = new Access(name, context.getEntityName(E), vn);
//=======
//>>>>>>> .r4421

//<<<<<<< .mine
      // (context.top() is current entity)
      // record the name as exposed for the current entity, since if the name is
      // updated via a call to a nested function, SSA for the current entity may
      // need to be updated with the new definition
//      addExposedName(context.top(), E, name, vn, false, context);
//=======
   */
      return lastIndex;
      markExposedInEnclosingEntities(context, name, definingScope, E, entityName, false);
//>>>>>>> .r4421

//<<<<<<< .mine
      // record the access; later, the Accesses in the instruction
      // defining vn will be adjusted based on this information; see
      // patchLexicalAccesses()
//      addAccess(context, context.top(), A);
//=======
      return S.valueNumber();
//>>>>>>> .r4421

    } else {
      // lexically-scoped variables should be read from their scope each time
      int result = context.currentScope().allocateTempValue();
//<<<<<<< .mine
//      Access A = new Access(name, context.getEntityName(E), result);
//=======
      Access A = new Access(name, entityName, result);
      context.cfg().addInstruction(new AstLexicalRead(context.cfg().currentInstruction, A));
      markExposedInEnclosingEntities(context, name, definingScope, E, entityName, false);
      return result;
    }
  }

  /**
   * record name as exposed for the current entity and for all enclosing
   * entities up to that of the defining scope, since if the name is updated via
   * a call to a nested function, SSA for these entities may need to be updated
   * with the new definition
   * 
   * @param context
   * @param name
   * @param definingScope
   * @param E
   * @param entityName
   * @param isWrite
   */
  private void markExposedInEnclosingEntities(WalkContext context, final String name, Scope definingScope, CAstEntity E,
      final String entityName, boolean isWrite) {
    Scope curScope = context.currentScope();

    while (!curScope.equals(definingScope)) {
      final Symbol curSymbol = curScope.lookup(name);
      final int vn = curSymbol.valueNumber();
      final Access A = new Access(name, entityName, vn);
      final CAstEntity entity = curScope.getEntity();
      if (entity != definingScope.getEntity()) {
        addExposedName(entity, E, name, vn, isWrite, context);
        // record the access; later, the Accesses in the instruction
        // defining vn will be adjusted based on this information; see
        // patchLexicalAccesses()
        addAccess(context, entity, A);
      }
      curScope = curScope.getParent();
    }
  }

  /**
   * Note that the caller is responsible for ensuring that name is defined in a
   * lexical scope.
   * 
   */
  protected void doLexicallyScopedWrite(WalkContext context, String name, int rval) {
    Symbol S = context.currentScope().lookup(name);
    Scope definingScope = S.getDefiningScope();
    CAstEntity E = definingScope.getEntity();
    // record in declaring scope that the name is exposed to a nested scope
    addExposedName(E, E, name, definingScope.lookup(name).valueNumber(), true, context);

    if (useLocalValuesForLexicalVars()) {
      // lexically-scoped variables can be given a single vn in a method
      
      markExposedInEnclosingEntities(context, name, definingScope, E, context.getEntityName(E), true);

      context.cfg().addInstruction(new AssignInstruction(context.cfg().currentInstruction, S.valueNumber(), rval));
      // we add write instructions at every access for now
      // eventually, we may restructure the method to do a single combined write
      // before exit
      Access A = new Access(name, context.getEntityName(E), rval);
      context.cfg().addInstruction(new AstLexicalWrite(context.cfg().currentInstruction, A));

    } else {
      // lexically-scoped variables must be written in their scope each time
      Access A = new Access(name, context.getEntityName(E), rval);
      context.cfg().addInstruction(new AstLexicalWrite(context.cfg().currentInstruction, A));
      markExposedInEnclosingEntities(context, name, definingScope, E, context.getEntityName(E), true);
    }
  }

  /**
   * generate instructions for a read of a global
  protected int doGlobalRead(CAstNode node, WalkContext context, String name) {
    Symbol S = context.currentScope().lookup(name);

    // Global variables can be treated as lexicals defined in the CG root, or
    if (treatGlobalsAsLexicallyScoped()) {

      // lexically-scoped variables can be given a single vn in a method, or
      if (useLocalValuesForLexicalVars()) {
        int vn = S.valueNumber();
        Access A = new Access(name, null, vn);

        addExposedName(context.top(), null, name, vn, false, context);
        addAccess(context, context.top(), A);

        return vn;

        // lexically-scoped variables can be read from their scope each time
      } else {
        int result = context.currentScope().allocateTempValue();
        Access A = new Access(name, null, result);
        context.cfg().addInstruction(new AstLexicalRead(context.cfg().currentInstruction, A));
        addAccess(context, context.top(), A);
        return result;
      }

      // globals can be treated as a single static location
    } else {
      int result = context.currentScope().allocateTempValue();
      FieldReference global = makeGlobalRef(name);
      context.cfg().addInstruction(new AstGlobalRead(context.cfg().currentInstruction, result, global));
      return result;
    }
  }

  /**
   * generate instructions for a write of a global
   */
  protected void doGlobalWrite(WalkContext context, String name, int rval) {
    Symbol S = context.currentScope().lookup(name);


    // Global variables can be treated as lexicals defined in the CG root, or
    if (treatGlobalsAsLexicallyScoped()) {

      // lexically-scoped variables can be given a single vn in a method, or
      if (useLocalValuesForLexicalVars()) {
        int vn = S.valueNumber();
        Access A = new Access(name, null, vn);

        addExposedName(context.top(), null, name, vn, true, context);
        addAccess(context, context.top(), A);

        context.cfg().addInstruction(new AssignInstruction(context.cfg().currentInstruction, vn, rval));
        context.cfg().addInstruction(new AstLexicalWrite(context.cfg().currentInstruction, A));

        // lexically-scoped variables can be read from their scope each time
      } else {
        Access A = new Access(name, null, rval);
        context.cfg().addInstruction(new AstLexicalWrite(context.cfg().currentInstruction, A));
        addAccess(context, context.top(), A);
      }

      // globals can be treated as a single static location
    } else {
      FieldReference global = makeGlobalRef(name);
      context.cfg().addInstruction(new AstGlobalWrite(context.cfg().currentInstruction, global, rval));
    }
  }

  /**
   * generate instructions to check if ref has field, storing answer in result
   */
  protected void doIsFieldDefined(WalkContext context, int result, int ref, CAstNode field) {
    Assertions.UNREACHABLE();
  }

  /**
   * creates a reference to a global named globalName. the declaring type and
   * type of the global are both the root type.
   */
  protected FieldReference makeGlobalRef(String globalName) {
    TypeReference rootTypeRef = TypeReference.findOrCreate(loader.getReference(), AstTypeReference.rootTypeName);
    return FieldReference.findOrCreate(rootTypeRef, Atom.findOrCreateUnicodeAtom("global " + globalName), rootTypeRef);
  }

  protected final IClassLoader loader;

  /**
   * for handling languages that let you include other source files named
   * statically (e.g., ABAP)
   */
  protected final Map namedEntityResolver;

  protected final SSAInstructionFactory insts;

  protected AstTranslator(IClassLoader loader, Map namedEntityResolver, ArrayOpHandler arrayOpHandler) {
    this.loader = loader;
    this.namedEntityResolver = namedEntityResolver;
    this.arrayOpHandler = arrayOpHandler!=null? arrayOpHandler: this;
    this.insts = loader.getInstructionFactory();
  }

  protected AstTranslator(IClassLoader loader, Map namedEntityResolver) {
    this(loader, namedEntityResolver, null);
  }
  
  protected AstTranslator(IClassLoader loader) {
    this(loader, null);
  }

  /**
   * for keeping position information for the generated SSAInstructions and SSA
   * locals
   */
  private static class AstDebuggingInformation implements DebuggingInformation {
    private Position codeBodyPosition;

    private String[][] valueNumberNames;

    private Position[] instructionPositions;

    AstDebuggingInformation(Position codeBodyPosition, Position[] instructionPositions, String[] names) {
      this.codeBodyPosition = codeBodyPosition;

      this.instructionPositions = instructionPositions;

      valueNumberNames = new String[names.length][];
      for (int i = 0; i < names.length; i++) {
        if (names[i] != null) {
          valueNumberNames[i] = new String[] { names[i] };
        } else {
          valueNumberNames[i] = new String[0];
        }
      }
    }

    public Position getCodeBodyPosition() {
      return codeBodyPosition;
    }
    public Position getInstructionPosition(int instructionOffset) {
      return instructionPositions[instructionOffset];
    }

    public String[][] getSourceNamesForValues() {
      return valueNumberNames;
    }
  }

  public static final boolean DEBUG_ALL = false;

  public static final boolean DEBUG_TOP = DEBUG_ALL || false;

  public static final boolean DEBUG_CFG = DEBUG_ALL || false;

  public static final boolean DEBUG_NAMES = DEBUG_ALL || false;

  public static final boolean DEBUG_LEXICAL = DEBUG_ALL || false;

  /**
   * basic block implementation used in the CFGs constructed during the
   * IR-generating AST traversal
   */
  protected final static class PreBasicBlock implements INodeWithNumber, IBasicBlock {
    private static final int NORMAL = 0;

    private static final int HANDLER = 1;

    private static final int ENTRY = 2;

    private static final int EXIT = 3;

    private int kind = NORMAL;

    private int number = -1;

    private int firstIndex = -1;

    private int lastIndex = -2;

    private final List instructions = new ArrayList();

    public int getNumber() {
      return getGraphNodeId();
    }

    public int getGraphNodeId() {
      return number;
    }

    public void setGraphNodeId(int number) {
      this.number = number;
    }

    public int getFirstInstructionIndex() {
      return firstIndex;
    }

    void setFirstIndex(int firstIndex) {
      this.firstIndex = firstIndex;
    }

    public int getLastInstructionIndex() {
    }

    void setLastIndex(int lastIndex) {
      this.lastIndex = lastIndex;
    }

    void makeExitBlock() {
      kind = EXIT;
    }

    void makeEntryBlock() {
      kind = ENTRY;
    }

    void makeHandlerBlock() {
      kind = HANDLER;
    }

    public boolean isEntryBlock() {
      return kind == ENTRY;
    }

    public boolean isExitBlock() {
      return kind == EXIT;
    }

    public boolean isHandlerBlock() {
      return kind == HANDLER;
    }

    public String toString() {
      return "PreBB" + number + ":" + firstIndex + ".." + lastIndex;
    }

    List instructions() {
      return instructions;
    }

    public boolean isCatchBlock() {
      return (lastIndex > -1) && (instructions.get(0) instanceof SSAGetCaughtExceptionInstruction);
    }

    public IMethod getMethod() {
      return null;
    }

    public Iterator iterator() {
      return instructions.iterator();
    }
  }

  protected final class UnwindState {
    final CAstNode unwindAst;

    final WalkContext astContext;

    final CAstVisitor astVisitor;

    UnwindState(CAstNode unwindAst, WalkContext astContext, CAstVisitor astVisitor) {
      this.unwindAst = unwindAst;
      this.astContext = astContext;
      this.astVisitor = astVisitor;
    }

    public UnwindState getParent() {
      return astContext.getUnwindState();
    }

    public int hashCode() {
      return astContext.hashCode() * unwindAst.hashCode() * astVisitor.hashCode();
    }

    public boolean equals(Object o) {
      if (o instanceof UnwindState) {
        if (((UnwindState) o).unwindAst != unwindAst)
          return false;
        if (((UnwindState) o).astVisitor != astVisitor)
          return false;
        if (getParent() == null) {
          return ((UnwindState) o).getParent() == null;
        } else {
          return getParent().equals(((UnwindState) o).getParent());
        }
      }

      return false;
    }

    boolean covers(UnwindState other) {
      if (equals(other))
        return true;
      if (getParent() != null)
        return getParent().covers(other);
      return false;
    }
  }

  /**
   * holds the control-flow graph as it is being constructed. When construction
   * is complete, information is stored in an {@link AstCFG}
   */
  public final class IncipientCFG extends SparseNumberedGraph {

    protected class Unwind {
      private final Map unwindData = new LinkedHashMap();

      /**
       * a cache of generated blocks
       */
      private final Map>, PreBasicBlock> code = new LinkedHashMap>, PreBasicBlock>();

      void setUnwindState(PreBasicBlock block, UnwindState context) {
        unwindData.put(block, context);
      }

      void setUnwindState(CAstNode node, UnwindState context) {
        unwindData.put(nodeToBlock.get(node), context);
      }

      /**
       * When adding an edge from source to target, it is possible that certain
       * exception-handling code needs to be executed before the control is
       * actually transfered to target. This method determines if this is the
       * case, and if so, it generates the exception handler blocks and adds an
       * appropriate edge to the target. It returns the basic block that should
       * be the target of the edge from source (target itself if there is no
       * exception-handling code, the initial catch block otherwise)
       */
      public PreBasicBlock findOrCreateCode(PreBasicBlock source, PreBasicBlock target, final boolean exception) {
        UnwindState sourceContext = unwindData.get(source);
        final CAstNode dummy = exception ? (new CAstImpl()).makeNode(CAstNode.EMPTY) : null;

        // no unwinding is needed, so jump to target block directly
        if (sourceContext == null)
          return target;

        WalkContext astContext = sourceContext.astContext;
        UnwindState targetContext = null;
        if (target != null)
          targetContext = unwindData.get(target);

        // in unwind context, but catch in same (or inner) unwind context
        if (targetContext != null && targetContext.covers(sourceContext))
          return target;

        Pair> key = Pair.make(sourceContext, Pair.make(target, exception));

        if (code.containsKey(key)) {
          return code.get(key);

        } else {
          int e = -1;
          PreBasicBlock currentBlock = getCurrentBlock();
          if (!isDeadBlock(currentBlock)) {
            addInstruction(insts.GotoInstruction(currentInstruction));
            newBlock(false);
          }
          PreBasicBlock startBlock = getCurrentBlock();
          if (exception) {
            setCurrentBlockAsHandler();
            e = sourceContext.astContext.currentScope().allocateTempValue();
            addInstruction(insts.GetCaughtExceptionInstruction(currentInstruction, startBlock.getNumber(), e));
            sourceContext.astContext.setCatchType(startBlock.getNumber(), defaultCatchType());
          }

          while (sourceContext != null && (targetContext == null || !targetContext.covers(sourceContext))) {
            final CAstRewriter.Rewrite ast = (new CAstCloner(new CAstImpl()) {
              protected CAstNode flowOutTo(Map, CAstNode> nodeMap, CAstNode oldSource, Object label,
                  CAstNode oldTarget, CAstControlFlowMap orig, CAstSourcePositionMap src) {
                if (exception && !isExceptionLabel(label)) {
                  return dummy;
                } else {
                  return oldTarget;
                }
              }
            }).copy(sourceContext.unwindAst, sourceContext.astContext.getControlFlow(), sourceContext.astContext.getSourceMap(),
                sourceContext.astContext.top().getNodeTypeMap(), sourceContext.astContext.top().getAllScopedEntities());
            sourceContext.astVisitor.visit(ast.newRoot(), new DelegatingContext(sourceContext.astContext) {
              public CAstSourcePositionMap getSourceMap() {
                return ast.newPos();
              }

              public CAstControlFlowMap getControlFlow() {
                return ast.newCfg();
              }
            }, sourceContext.astVisitor);

            sourceContext = sourceContext.getParent();
          }

          PreBasicBlock endBlock = getCurrentBlock();
          if (exception) {
            addPreNode(dummy);
            doThrow(astContext, e);
          } else {
            addInstruction(insts.GotoInstruction(currentInstruction));
          }
          newBlock(false);

          addEdge(currentBlock, getCurrentBlock());
          if (target != null) {
            addEdge(endBlock, target);

            // `null' target is idiom for branch/throw to exit
          } else {
            addDelayedEdge(endBlock, exitMarker, exception);
          }

          code.put(key, startBlock);
          return startBlock;
        }
      }
    }

    private Unwind unwind = null;

    private final List blocks = new ArrayList();

    private final Map nodeToBlock = new LinkedHashMap();

    private final Map>> delayedEdges = new LinkedHashMap>>();

    private final Object exitMarker = new Object();

    private final Set deadBlocks = new LinkedHashSet();

    private final Set normalToExit = new LinkedHashSet();

    private final Set exceptionalToExit = new LinkedHashSet();

    private Position[] linePositions = new Position[10];

    private boolean hasCatchBlock = false;

    /**
     * does the method have any monitor operations?
     */
    private boolean hasMonitorOp = false;

    private int currentInstruction = 0;

    private PreBasicBlock currentBlock;
    public int getCurrentInstruction() {
      return currentInstruction;
    }

    public PreBasicBlock getCurrentBlock() {
      return currentBlock;
    }

    boolean hasCatchBlock() {
      return hasCatchBlock;
    }

    boolean hasMonitorOp() {
      return hasMonitorOp;
    }

    void noteCatchBlock() {
      hasCatchBlock = true;
    }

    Position[] getLinePositionMap() {
      return linePositions;
    }

    /**
     * create a new basic block, and set it as the current block.
     * 
     * @param fallThruFromPrior
     *          should a fall-through edge be added from the previous block
     *          (value of currentBlock at entry)? if false, the newly created
     *          block is marked as a dead block, as it has no incoming edges.
     * @return the new block
     */
    public PreBasicBlock newBlock(boolean fallThruFromPrior) {
      // optimization: if we have a fall-through from an empty block, just
      // return the empty block
      if (fallThruFromPrior && !currentBlock.isEntryBlock() && currentBlock.instructions().size() == 0) {
        return currentBlock;
      }

      PreBasicBlock previous = currentBlock;
      currentBlock = new PreBasicBlock();
      addNode(currentBlock);
      blocks.add(currentBlock);

      if (DEBUG_CFG)
        System.err.println(("adding new block (node) " + currentBlock));
      if (fallThruFromPrior) {
        if (DEBUG_CFG)
          System.err.println(("adding fall-thru edge " + previous + " --> " + currentBlock));
        addEdge(previous, currentBlock);
      } else {
        deadBlocks.add(currentBlock);
      }

      return currentBlock;
    }

    /**
     * record a delayed edge addition from src to dst. Edge will be added when

     * appropriate; see {@link #checkForRealizedEdges(CAstNode)} and
     * {@link #checkForRealizedExitEdges(PreBasicBlock)}
     */
    private void addDelayedEdge(PreBasicBlock src, Object dst, boolean exception) {
      MapUtil.findOrCreateSet(delayedEdges, dst).add(Pair.make(src, exception));
    }

    void makeEntryBlock(PreBasicBlock bb) {
      bb.makeEntryBlock();
    }

    void makeExitBlock(PreBasicBlock bb) {
      bb.makeExitBlock();

      for (Iterator ps = getPredNodes(bb); ps.hasNext();)
        normalToExit.add(ps.next());

      // now that we have created the exit block, add the delayed edges to the
      // exit
      checkForRealizedExitEdges(bb);
    }

    void setCurrentBlockAsHandler() {
      currentBlock.makeHandlerBlock();
    }

    boolean hasDelayedEdges(CAstNode n) {
      return delayedEdges.containsKey(n);
    }

    /**
     * given some n which is now mapped by nodeToBlock, add any delayed edges to
     * n's block
     */
    private void checkForRealizedEdges(CAstNode n) {
      if (delayedEdges.containsKey(n)) {
        for (Iterator> ss = delayedEdges.get(n).iterator(); ss.hasNext();) {
          Pair s = ss.next();
          PreBasicBlock src = s.fst;
          boolean exception = s.snd;
          if (unwind == null) {
            addEdge(src, nodeToBlock.get(n));
          } else {
    }
            PreBasicBlock target = nodeToBlock.get(n);
            addEdge(src, unwind.findOrCreateCode(src, target, exception));
          }
        }

        delayedEdges.remove(n);
      }
    }

    /**
     * add any delayed edges to the exit block
     */
    private void checkForRealizedExitEdges(PreBasicBlock exitBlock) {
      if (delayedEdges.containsKey(exitMarker)) {
        for (Iterator> ss = delayedEdges.get(exitMarker).iterator(); ss.hasNext();) {
          Pair s = ss.next();
          PreBasicBlock src = s.fst;
          boolean exception = s.snd;
          addEdge(src, exitBlock);
          if (exception)
            exceptionalToExit.add(src);
          else
            normalToExit.add(src);
        }

        delayedEdges.remove(exitMarker);
      }
    }

    private void setUnwindState(CAstNode node, UnwindState context) {
      if (unwind == null)
        unwind = new Unwind();
      unwind.setUnwindState(node, context);
    }

    public void addPreNode(CAstNode n) {
      addPreNode(n, null);
    }

    /**
     * associate n with the current block, and update the current unwind state
     */
    public void addPreNode(CAstNode n, UnwindState context) {
      if (DEBUG_CFG)
        System.err.println(("adding pre-node " + n));
      nodeToBlock.put(n, currentBlock);
      deadBlocks.remove(currentBlock);
      if (context != null)
        setUnwindState(n, context);
      // now that we've associated n with a block, add associated delayed edges
      checkForRealizedEdges(n);
    }

    public void addPreEdge(CAstNode src, CAstNode dst, boolean exception) {
      assert nodeToBlock.containsKey(src);
      addPreEdge(nodeToBlock.get(src), dst, exception);
    }

    /**
     * if dst is associated with a basic block b, add an edge from src to b.
     * otherwise, record the edge addition as delayed.
     */
    public void addPreEdge(PreBasicBlock src, CAstNode dst, boolean exception) {
      if (dst == CAstControlFlowMap.EXCEPTION_TO_EXIT) {
        assert exception;
        addPreEdgeToExit(src, exception);
      } else if (nodeToBlock.containsKey(dst)) {
        PreBasicBlock target = nodeToBlock.get(dst);
        if (DEBUG_CFG)
          System.err.println(("adding pre-edge " + src + " --> " + dst));
        if (unwind == null) {
          addEdge(src, target);
        } else {
          addEdge(src, unwind.findOrCreateCode(src, target, exception));
        }
      } else {
        if (DEBUG_CFG)
          System.err.println(("adding delayed pre-edge " + src + " --> " + dst));
        addDelayedEdge(src, dst, exception);
      }
    }

    public void addPreEdgeToExit(CAstNode src, boolean exception) {
      assert nodeToBlock.containsKey(src);
      addPreEdgeToExit(nodeToBlock.get(src), exception);
    }

    public void addPreEdgeToExit(PreBasicBlock src, boolean exception) {
      if (unwind != null) {
        PreBasicBlock handlers = unwind.findOrCreateCode(src, null, exception);
        if (handlers != null) {
          addEdge(src, handlers);
          return;
        }
      }

      addDelayedEdge(src, exitMarker, exception);
    }

    public void addEdge(PreBasicBlock src, PreBasicBlock dst) {
      super.addEdge(src, dst);
      deadBlocks.remove(dst);

    boolean isDeadBlock(PreBasicBlock block) {
      return deadBlocks.contains(block);
    }

    public PreBasicBlock getBlock(CAstNode n) {
      return nodeToBlock.get(n);
    }

    /**
     * mark the current position as the position for the instruction
     */
    private void noteLinePosition(int instruction) {
      if (linePositions.length < (instruction + 1)) {
        Position[] newData = new Position[instruction * 2 + 1];
        System.arraycopy(linePositions, 0, newData, 0, linePositions.length);
        linePositions = newData;
      }

      linePositions[instruction] = getCurrentPosition();
    }

    public void addInstruction(SSAInstruction n) {
      deadBlocks.remove(currentBlock);

      int inst = currentInstruction++;

      noteLinePosition(inst);

      if (currentBlock.instructions().size() == 0) {
        currentBlock.setFirstIndex(inst);
      } else {
        assert !(n instanceof SSAGetCaughtExceptionInstruction);
      }

      if (DEBUG_CFG) {
        System.err.println(("adding " + n + " at " + inst + " to " + currentBlock));
      }

      if (n instanceof SSAMonitorInstruction) {
        hasMonitorOp = true;
      }

      currentBlock.instructions().add(n);

      currentBlock.setLastIndex(inst);
    }
  }

  /**
   * data structure for the final CFG for a method, based on the information in
   * an {@link IncipientCFG}
   */
  protected final static class AstCFG extends AbstractCFG {
    private final SSAInstruction[] instructions;

    private final int[] instructionToBlockMap;

    private final String functionName;

    private final SymbolTable symtab;
    AstCFG(CAstEntity n, IncipientCFG icfg, SymbolTable symtab) {
      super(null);
      List blocks = icfg.blocks;

      this.symtab = symtab;
      functionName = n.getName();
      instructionToBlockMap = new int[blocks.size()];

      for (int i = 0; i < blocks.size(); i++)
        instructionToBlockMap[i] = blocks.get(i).getLastInstructionIndex();

      for (int i = 0; i < blocks.size(); i++) {
        PreBasicBlock block = blocks.get(i);
        this.addNode(block);
        if (block.isCatchBlock()) {
          setCatchBlock(i);
        }

        if (DEBUG_CFG)
          System.err.println(("added " + blocks.get(i) + " to final CFG as " + getNumber(blocks.get(i))));
      }
      if (DEBUG_CFG)
        System.err.println((getMaxNumber() + " blocks total"));

      init();

      for (int i = 0; i < blocks.size(); i++) {
        PreBasicBlock src = blocks.get(i);
        for (Iterator j = icfg.getSuccNodes(src); j.hasNext();) {
          PreBasicBlock dst = (PreBasicBlock) j.next();
          if (isCatchBlock(dst.getNumber()) || (dst.isExitBlock() && icfg.exceptionalToExit.contains(src))) {
            if (DEBUG_CFG)
              System.err.println(("exceptonal edge " + src + " -> " + dst));
            addExceptionalEdge(src, dst);
          }

          if (dst.isExitBlock() ? icfg.normalToExit.contains(src) : !isCatchBlock(dst.getNumber())) {
            if (DEBUG_CFG)
              System.err.println(("normal edge " + src + " -> " + dst));
            addNormalEdge(src, dst);
          }
        }
      }

      int x = 0;
      instructions = new SSAInstruction[icfg.currentInstruction];
      for (int i = 0; i < blocks.size(); i++) {
        List bi = blocks.get(i).instructions();
        for (int j = 0; j < bi.size(); j++) {
          instructions[x++] = bi.get(j);
        }
      }
    }

    public int hashCode() {
      return functionName.hashCode();
    }

    public boolean equals(Object o) {
      return (o instanceof AstCFG) && functionName.equals(((AstCFG) o).functionName);
    }

    public PreBasicBlock getBlockForInstruction(int index) {
      for (int i = 1; i < getNumberOfNodes() - 1; i++)
        if (index <= instructionToBlockMap[i])
          return getNode(i);

      return null;
    }

    public SSAInstruction[] getInstructions() {
      return instructions;
    }

    public int getProgramCounter(int index) {
      return index;
    }

    public String toString() {
      SSAInstruction[] insts = (SSAInstruction[]) getInstructions();
      StringBuffer s = new StringBuffer("CAst CFG of " + functionName);
      int params[] = symtab.getParameterValueNumbers();
      for (int i = 0; i < params.length; i++)
        s.append(" ").append(params[i]);
      s.append("\n");

      for (int i = 0; i < getNumberOfNodes(); i++) {
        PreBasicBlock bb = (PreBasicBlock) getNode(i);
        s.append(bb).append("\n");

        for (Iterator ss = getSuccNodes(bb); ss.hasNext();)
          s.append("    -->" + ss.next() + "\n");

        for (int j = bb.getFirstInstructionIndex(); j <= bb.getLastInstructionIndex(); j++)
          if (insts[j] != null)
            s.append("  " + insts[j].toString(symtab) + "\n");
      }

      s.append("-- END --");
      return s.toString();
    }
  }

  public static enum ScopeType {
    LOCAL, GLOBAL, SCRIPT, FUNCTION, TYPE
  };

  private static final boolean DEBUG = false;

  protected class FinalCAstSymbol implements CAstSymbol {
    private final String _name;

    private FinalCAstSymbol(String _name) {
      this._name = _name;
    }

    public String name() {
      return _name;
    }

    public boolean isFinal() {
      return true;
    }

    public boolean isCaseInsensitive() {
      return false;
    }

    public boolean isInternalName() {
      return false;
    }

    public Object defaultInitValue() {
      return null;
    }
  }

  public static class InternalCAstSymbol extends CAstSymbolImplBase {
    public InternalCAstSymbol(String _name) {
      super(_name, false, false, null);
    }

    public InternalCAstSymbol(String _name, boolean _isFinal) {
      super(_name, _isFinal, false, null);
    }

    public InternalCAstSymbol(String _name, boolean _isFinal, boolean _isCaseInsensitive) {
      super(_name, _isFinal, _isCaseInsensitive, null);
    }


    public int size() {
    public InternalCAstSymbol(String _name, boolean _isFinal, boolean _isCaseInsensitive, Object _defaultInitValue) {
      super(_name, _isFinal, _isCaseInsensitive, _defaultInitValue);
    }

    public boolean isInternalName() {
      return true;
    }
  }

  /**
   * interface for name information stored in a symbol table.
   * 
   * @see Scope
   */
  protected interface Symbol {
    int valueNumber();

    Scope getDefiningScope();

    boolean isParameter();

    Object constant();

    void setConstant(Object s);

    boolean isFinal();

    boolean isInternalName();

    Object defaultInitValue();
  }

  /**
   * a scope in the symbol table built during AST traversal
   */
  public interface Scope {

    ScopeType type();

    int allocateTempValue();

    int getConstantValue(Object c);

    boolean isConstant(int valueNumber);

    Object getConstantObject(int valueNumber);

    void declare(CAstSymbol s);

    void declare(CAstSymbol s, int valueNumber);

    boolean isCaseInsensitive(String name);

    boolean contains(String name);

    Symbol lookup(String name);

    Iterator getAllNames();

    int size();

    boolean isGlobal(Symbol s);

    boolean isLexicallyScoped(Symbol s);

    CAstEntity getEntity();

    Scope getParent();
  }

  private static abstract class AbstractSymbol implements Symbol {
    private Object constantValue;

    private boolean isFinalValue;

    private final Scope definingScope;

    private Object defaultValue;

    AbstractSymbol(Scope definingScope, boolean isFinalValue, Object defaultValue) {
      this.definingScope = definingScope;
      this.isFinalValue = isFinalValue;
      this.defaultValue = defaultValue;
    }

    public boolean isFinal() {
      return isFinalValue;
    }

    public Object defaultInitValue() {
      return defaultValue;
    }

    public Object constant() {
      return constantValue;
    }

    public void setConstant(Object cv) {
      constantValue = cv;
    }

    public Scope getDefiningScope() {
      return definingScope;
    }
  };

  private abstract class AbstractScope implements Scope {
    private final Scope parent;

    private final Map values = new LinkedHashMap();

    private final Map caseInsensitiveNames = new LinkedHashMap();

    protected abstract SymbolTable getUnderlyingSymtab();

    public Scope getParent() {
      return parent;
    }
      return getUnderlyingSymtab().getMaxValueNumber() + 1;
    }

    public Iterator getAllNames() {
      return values.keySet().iterator();
    }

    public int allocateTempValue() {
      return getUnderlyingSymtab().newSymbol();
    }

    public int getConstantValue(Object o) {
      if (o instanceof Integer) {
        return getUnderlyingSymtab().getConstant(((Integer) o).intValue());
      } else if (o instanceof Float) {
        return getUnderlyingSymtab().getConstant(((Float) o).floatValue());
      } else if (o instanceof Double) {
        return getUnderlyingSymtab().getConstant(((Double) o).doubleValue());
      } else if (o instanceof Long) {
        return getUnderlyingSymtab().getConstant(((Long) o).longValue());
      } else if (o instanceof String) {
        return getUnderlyingSymtab().getConstant((String) o);
      } else if (o instanceof Boolean) {
        return getUnderlyingSymtab().getConstant((Boolean) o);
      } else if (o instanceof Character) {
        return getUnderlyingSymtab().getConstant(((Character) o).charValue());
      } else if (o instanceof Byte) {
        return getUnderlyingSymtab().getConstant(((Byte) o).byteValue());
      } else if (o instanceof Short) {
        return getUnderlyingSymtab().getConstant(((Short) o).shortValue());
      } else if (o == null) {
        return getUnderlyingSymtab().getNullConstant();
      } else if (o == CAstControlFlowMap.SWITCH_DEFAULT) {
        return getUnderlyingSymtab().getConstant("__default label");
      } else {
        System.err.println(("cannot handle constant " + o));
        Assertions.UNREACHABLE();
        return -1;
      }
    }

    public boolean isConstant(int valueNumber) {
      return getUnderlyingSymtab().isConstant(valueNumber);
    }

    public Object getConstantObject(int valueNumber) {
      return getUnderlyingSymtab().getConstantValue(valueNumber);
    }

    public void declare(CAstSymbol s, int vn) {
      String nm = s.name();
      assert !contains(nm) : nm;
      if (s.isCaseInsensitive())
        caseInsensitiveNames.put(nm.toLowerCase(), nm);
      values.put(nm, makeSymbol(s, vn));
    }

    public void declare(CAstSymbol s) {
      String nm = s.name();
      if (!contains(nm) || lookup(nm).getDefiningScope() != this) {
        if (s.isCaseInsensitive())
          caseInsensitiveNames.put(nm.toLowerCase(), nm);
        values.put(nm, makeSymbol(s));
      } else {
        assert !s.isFinal() : "trying to redeclare " + nm;
      }
    }

    AbstractScope(Scope parent) {
      this.parent = parent;
    }

    private final String mapName(String nm) {
      String mappedName = caseInsensitiveNames.get(nm.toLowerCase());
      return (mappedName == null) ? nm : mappedName;
    }

    protected Symbol makeSymbol(CAstSymbol s) {
      return makeSymbol(s.name(), s.isFinal(), s.isInternalName(), s.defaultInitValue(), -1, this);
    }

    protected Symbol makeSymbol(CAstSymbol s, int vn) {
      return makeSymbol(s.name(), s.isFinal(), s.isInternalName(), s.defaultInitValue(), vn, this);
    }

    abstract protected Symbol makeSymbol(String nm, boolean isFinal, boolean isInternalName, Object defaultInitValue, int vn,
        Scope parent);

    public boolean isCaseInsensitive(String nm) {
      return caseInsensitiveNames.containsKey(nm.toLowerCase());
    }

    public Symbol lookup(String nm) {
      if (contains(nm)) {
        return values.get(mapName(nm));
      } else {
        Symbol scoped = parent.lookup(nm);
        if (scoped != null && getEntityScope() == this && (isGlobal(scoped) || isLexicallyScoped(scoped))) {
          values.put(nm,
              makeSymbol(nm, scoped.isFinal(), scoped.isInternalName(), scoped.defaultInitValue(), -1, scoped.getDefiningScope()));
          if (scoped.getDefiningScope().isCaseInsensitive(nm)) {
            caseInsensitiveNames.put(nm.toLowerCase(), nm);
          }
          return values.get(nm);
        } else {
          return scoped;
        }
      }
    }

    public boolean contains(String nm) {
      String mappedName = caseInsensitiveNames.get(nm.toLowerCase());
      return values.containsKey(mappedName == null ? nm : mappedName);
    }

    public boolean isGlobal(Symbol s) {
      return s.getDefiningScope().type() == ScopeType.GLOBAL;
    }

    public abstract boolean isLexicallyScoped(Symbol s);

    protected abstract AbstractScope getEntityScope();

    public abstract CAstEntity getEntity();
  };

  private AbstractScope makeScriptScope(final CAstEntity s, Scope parent) {
    return new AbstractScope(parent) {
      SymbolTable scriptGlobalSymtab = new SymbolTable(s.getArgumentCount());

      public SymbolTable getUnderlyingSymtab() {
        return scriptGlobalSymtab;
      }

      protected AbstractScope getEntityScope() {
        return this;
      }

      public boolean isLexicallyScoped(Symbol s) {
        if (isGlobal(s))
          return false;
        else
          return ((AbstractScope) s.getDefiningScope()).getEntity() != getEntity();
      }

      public CAstEntity getEntity() {
        return s;
      }

      public ScopeType type() {
        return ScopeType.SCRIPT;
      }

      protected Symbol makeSymbol(final String nm, final boolean isFinal, final boolean isInternalName,
          final Object defaultInitValue, int vn, Scope definer) {
        return false;
        final int v = vn == -1 ? getUnderlyingSymtab().newSymbol() : vn;
        if (useDefaultInitValues() && defaultInitValue != null) {
          if (getUnderlyingSymtab().getValue(v) == null) {
            setDefaultValue(getUnderlyingSymtab(), v, defaultInitValue);
          }
        }
        return new AbstractSymbol(definer, isFinal, defaultInitValue) {
          public String toString() {
            return nm + ":" + System.identityHashCode(this);
          }

          public int valueNumber() {
            return v;
          }

          public boolean isInternalName() {
            return isInternalName;
          }

          public boolean isParameter() {
            return false;
          }
        };
      }
    };
  }

  protected int getArgumentCount(CAstEntity f) {
    return f.getArgumentCount();
  }
  
  protected String[] getArgumentNames(CAstEntity f) {
    return f.getArgumentNames();
  }
  
  private AbstractScope makeFunctionScope(final CAstEntity f, Scope parent) {
    return new AbstractScope(parent) {
      private final String[] params = getArgumentNames(f);

      private final SymbolTable functionSymtab = new SymbolTable(getArgumentCount(f));

      // ctor for scope object
      {
        for (int i = 0; i < getArgumentCount(f); i++) {
          final int yuck = i;
          declare(new CAstSymbol() {
            public String name() {
              return params[yuck];
            }

            public boolean isFinal() {
              return false;
            }

            public boolean isCaseInsensitive() {
              return false;
            }

            public boolean isInternalName() {
              return false;
            }

            public Object defaultInitValue() {
              return null;
            }

          });
        }
      }

      public SymbolTable getUnderlyingSymtab() {
        return functionSymtab;
      }

      protected AbstractScope getEntityScope() {
        return this;
      }

      public boolean isLexicallyScoped(Symbol s) {
        if (isGlobal(s))
          return false;
        else
          return ((AbstractScope) s.getDefiningScope()).getEntity() != getEntity();
      }

      public CAstEntity getEntity() {
        return f;
      }

      public ScopeType type() {
        return ScopeType.FUNCTION;
      }

      private int find(String n) {
        for (int i = 0; i < params.length; i++) {
          if (n.equals(params[i])) {
            return i + 1;
          }
        }

        return -1;
      }

      protected Symbol makeSymbol(final String nm, final boolean isFinal, final boolean isInternalName,
          final Object defaultInitValue, final int valueNumber, Scope definer) {
        return new AbstractSymbol(definer, isFinal, defaultInitValue) {
          final int vn;

          {
            int x = find(nm);
            if (x != -1) {
              assert valueNumber == -1;
              vn = x;
            } else if (valueNumber != -1) {
              vn = valueNumber;
            } else {
              vn = getUnderlyingSymtab().newSymbol();
            }
            if (useDefaultInitValues() && defaultInitValue != null) {
              if (getUnderlyingSymtab().getValue(vn) == null) {
                setDefaultValue(getUnderlyingSymtab(), vn, defaultInitValue);
              }
            }
          }

          public String toString() {
            return nm + ":" + System.identityHashCode(this);
          }

          public int valueNumber() {
            return vn;
          }

          public boolean isInternalName() {
            return isInternalName;
          }

          public boolean isParameter() {
            return vn <= params.length;
          }
        };
      }
    };
  }

  private Scope makeLocalScope(CAstNode s, final Scope parent) {
    return new AbstractScope(parent) {
      public ScopeType type() {
        return ScopeType.LOCAL;
      }

      public SymbolTable getUnderlyingSymtab() {
        return ((AbstractScope) parent).getUnderlyingSymtab();
      }

      protected AbstractScope getEntityScope() {

        return ((AbstractScope) parent).getEntityScope();
      }

      public boolean isLexicallyScoped(Symbol s) {
        return ((AbstractScope) getEntityScope()).isLexicallyScoped(s);
      }

      public CAstEntity getEntity() {
        return ((AbstractScope) getEntityScope()).getEntity();
      }

      protected Symbol makeSymbol(final String nm, boolean isFinal, final boolean isInternalName, final Object defaultInitValue,
          int vn, Scope definer) {
        final int v = vn == -1 ? getUnderlyingSymtab().newSymbol() : vn;
        if (useDefaultInitValues() && defaultInitValue != null) {
          if (getUnderlyingSymtab().getValue(v) == null) {
            setDefaultValue(getUnderlyingSymtab(), v, defaultInitValue);
          }
        }
        return new AbstractSymbol(definer, isFinal, defaultInitValue) {
          public String toString() {
            return nm + ":" + System.identityHashCode(this);
          }

          public int valueNumber() {
            return v;
          }

          public boolean isInternalName() {
            return isInternalName;
          }

          public boolean isParameter() {
            return false;
          }
        };
      }
    };
  }

  private Scope makeGlobalScope() {
    final Map globalSymbols = new LinkedHashMap();
    final Map caseInsensitiveNames = new LinkedHashMap();
    return new Scope() {
      private final String mapName(String nm) {
        String mappedName = caseInsensitiveNames.get(nm.toLowerCase());
        return (mappedName == null) ? nm : mappedName;
      }

      public Scope getParent() {
        return null;
      }

      public boolean isGlobal(Symbol s) {
        return true;
      }

      public boolean isLexicallyScoped(Symbol s) {
      }

      public CAstEntity getEntity() {
        return null;
      }

      public int size() {
        return globalSymbols.size();
      }

      public Iterator getAllNames() {
        return globalSymbols.keySet().iterator();
      }

      public int allocateTempValue() {
        throw new UnsupportedOperationException();
      }

      public int getConstantValue(Object c) {
        throw new UnsupportedOperationException();
      }

      public boolean isConstant(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public Object getConstantObject(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public ScopeType type() {
        return ScopeType.GLOBAL;
      }

      public boolean contains(String name) {
        return hasImplicitGlobals() || globalSymbols.containsKey(mapName(name));
      }

      public boolean isCaseInsensitive(String name) {
        return caseInsensitiveNames.containsKey(name.toLowerCase());
      }

      public Symbol lookup(final String name) {
        if (!globalSymbols.containsKey(mapName(name))) {
          if (hasImplicitGlobals()) {
            declare(new CAstSymbol() {
      return name;
              public String name() {
                return name;
              }

              public boolean isFinal() {
                return false;
              }

              public boolean isCaseInsensitive() {
                return false;
              }

              public boolean isInternalName() {
                return false;
              }

              public Object defaultInitValue() {
                return null;
              }
            });
          } else if (hasSpecialUndeclaredVariables()) {
            return null;
          } else {
            throw new Error("cannot find " + name);
          }
        }

        return globalSymbols.get(mapName(name));
      }

      public void declare(CAstSymbol s, int vn) {
        assert vn == -1;
        declare(s);
      }

      public void declare(final CAstSymbol s) {
        final String name = s.name();
        if (s.isCaseInsensitive()) {
          caseInsensitiveNames.put(name.toLowerCase(), name);
        }
        globalSymbols.put(name, new AbstractSymbol(this, s.isFinal(), s.defaultInitValue()) {
          public String toString() {
            return name + ":" + System.identityHashCode(this);
          }

          public boolean isParameter() {
            return false;
          }

          public boolean isInternalName() {
            return s.isInternalName();
          }

          public int valueNumber() {
            throw new UnsupportedOperationException();
          }
        });
      }
    };
  }

  protected Scope makeTypeScope(final CAstEntity type, final Scope parent) {
    Scope currentScope();

    final Map typeSymbols = new LinkedHashMap();
    final Map caseInsensitiveNames = new LinkedHashMap();
    return new Scope() {
      private final String mapName(String nm) {
        String mappedName = caseInsensitiveNames.get(nm.toLowerCase());
        return (mappedName == null) ? nm : mappedName;
      }

      public Scope getParent() {
        return parent;
      }

      public boolean isGlobal(Symbol s) {
        return false;
      }

      public boolean isLexicallyScoped(Symbol s) {
        return false;
      }

      public CAstEntity getEntity() {
        return type;
      }

      public int size() {
        return typeSymbols.size();
      }

      public Iterator getAllNames() {
        return typeSymbols.keySet().iterator();
      }

      public int allocateTempValue() {
        throw new UnsupportedOperationException();
      }

      public int getConstantValue(Object c) {
        throw new UnsupportedOperationException();
      }

      public boolean isConstant(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public Object getConstantObject(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public ScopeType type() {
        return ScopeType.TYPE;
      }

      public boolean contains(String name) {
        return typeSymbols.containsKey(mapName(name));
      }

      public boolean isCaseInsensitive(String name) {
        return caseInsensitiveNames.containsKey(name.toLowerCase());
      }

      public Symbol lookup(String nm) {
        if (typeSymbols.containsKey(mapName(nm)))
          return typeSymbols.get(mapName(nm));
        else {
          return parent.lookup(nm);
        }
      }

      public void declare(CAstSymbol s, int vn) {
        assert vn == -1;
        declare(s);
      }

      public void declare(final CAstSymbol s) {
        final String name = s.name();
        assert !s.isFinal();
        if (s.isCaseInsensitive())
          caseInsensitiveNames.put(name.toLowerCase(), name);
        typeSymbols.put(name, new AbstractSymbol(this, s.isFinal(), s.defaultInitValue()) {
          public String toString() {
            return name + ":" + System.identityHashCode(this);
          }

          public boolean isParameter() {
            return false;
          }

          public boolean isInternalName() {
            return s.isInternalName();
          }

          public int valueNumber() {
            throw new UnsupportedOperationException();
          }
        });
      }
    };
  }

  public interface WalkContext extends CAstVisitor.Context {

    ModuleEntry getModule();

    String getName();

    String file();

    CAstSourcePositionMap getSourceMap();

    CAstControlFlowMap getControlFlow();

    Set entityScopes();

    IncipientCFG cfg();

    UnwindState getUnwindState();

    void setCatchType(int blockNumber, TypeReference catchType);

    void setCatchType(CAstNode catchNode, TypeReference catchType);

    TypeReference[][] getCatchTypes();

    void addEntityName(CAstEntity e, String name);
    
    String getEntityName(CAstEntity e);

    boolean hasValue(CAstNode n);

    int setValue(CAstNode n, int v);

    int getValue(CAstNode n);
    
    Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet);
    
    Set getAccesses(CAstEntity e);
    
    Scope getGlobalScope();
    
  }

  private abstract class DelegatingContext implements WalkContext {
    private final WalkContext parent;

    DelegatingContext(WalkContext parent) {
      this.parent = parent;
    }

    public Set getAccesses(CAstEntity e) {
      return parent.getAccesses(e);
    }

    public ModuleEntry getModule() {
      return parent.getModule();
    }

    public String getName() {
      return parent.getName();
    }
    public String file() {
      return parent.file();
    }

    public CAstEntity top() {
      return parent.top();
    }

    public CAstSourcePositionMap getSourceMap() {
      return parent.getSourceMap();
    }

    public CAstControlFlowMap getControlFlow() {
      return parent.getControlFlow();
    }

    public Scope currentScope() {
      return parent.currentScope();
    }

    public Set entityScopes() {
      return parent.entityScopes();
    }

    public IncipientCFG cfg() {
      return parent.cfg();
    }

    public UnwindState getUnwindState() {
      return parent.getUnwindState();
    }

    public void setCatchType(int blockNumber, TypeReference catchType) {
      parent.setCatchType(blockNumber, catchType);
    }

    public void setCatchType(CAstNode catchNode, TypeReference catchType) {
      parent.setCatchType(catchNode, catchType);
    }

    public TypeReference[][] getCatchTypes() {
      return parent.getCatchTypes();
    }

    public void addEntityName(CAstEntity e, String name) {
      parent.addEntityName(e, name);
    }
    
    public String getEntityName(CAstEntity e) {
      return parent.getEntityName(e);
    }
    
    public boolean hasValue(CAstNode n) {
      return parent.hasValue(n);
    }

    public int setValue(CAstNode n, int v) {
      return parent.setValue(n, v);
    }

    public int getValue(CAstNode n) {
      return parent.getValue(n);
    }

    public Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet) {
    } else {
      return parent.exposeNameSet(entity, writeSet);
    }

    public Scope getGlobalScope() {
      return parent.getGlobalScope();
    }
    
  }

  private class FileContext extends DelegatingContext {
    private final String fUnitName;

    public FileContext(WalkContext parent, String unitName) {
      super(parent);
      fUnitName = unitName;
    }

    public String getName() {
      return fUnitName;
    }
  }

  private class UnwindContext extends DelegatingContext {
    private final UnwindState state;

    UnwindContext(CAstNode unwindNode, WalkContext parent, CAstVisitor visitor) {
      super(parent);
      this.state = new UnwindState(unwindNode, parent, visitor);
    }

    public UnwindState getUnwindState() {
      return state;
    }
  }

  private abstract class EntityContext extends DelegatingContext {
    protected final CAstEntity topNode;

    protected final String name;

    EntityContext(WalkContext parent, CAstEntity s) {
      super(parent);
      this.topNode = s;
      this.name = composeEntityName(parent, s);
      addEntityName(s, this.name);
    }

    public String getName() {
    }

    public CAstEntity top() {
      return topNode;
    }

    public CAstSourcePositionMap getSourceMap() {
      return top().getSourceMap();
    }

  }

  private class CodeEntityContext extends EntityContext {
    private final Scope topEntityScope;

    private final Set allEntityScopes;

    private final IncipientCFG cfg;

    private TypeReference[][] catchTypes = new TypeReference[0][];

    Set, Integer>> exposedReads;
    Set, Integer>> exposedWrites;
    
    Set accesses;
    
    /**
     * maps nodes in the current function to the value number holding their value
     * or, for constants, to their constant value.
     */
    private final Map results = new LinkedHashMap();

    CodeEntityContext(WalkContext parent, Scope entityScope, CAstEntity s) {
      super(parent, s);

      this.topEntityScope = entityScope;

      this.allEntityScopes = HashSetFactory.make();
      this.allEntityScopes.add(entityScope);

      cfg = new IncipientCFG();
    }

    public Set getAccesses(CAstEntity e) {
      if (e == topNode) {
        if (accesses == null) {
          accesses = HashSetFactory.make();
        }
        return accesses;
      } else {
        return super.getAccesses(e);
      }
    }
    
    @Override
    public Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet) {
      if (entity == topNode) {
       if (writeSet) {
         if (exposedWrites == null) {
           exposedWrites = HashSetFactory.make();
         }
         return exposedWrites;
       } else {
         if (exposedReads == null) {
           exposedReads = HashSetFactory.make();
         }
         return exposedReads;         
       }
      } else {
        return super.exposeNameSet(entity, writeSet);
      }
    }


    public CAstControlFlowMap getControlFlow() {
      return top().getControlFlow();
    }

    public IncipientCFG cfg() {
      return cfg;
    }

    public Scope currentScope() {
      return topEntityScope;
    }

    public Set entityScopes() {
      return allEntityScopes;
    }

    public UnwindState getUnwindState() {
      return null;
    }

    public void setCatchType(CAstNode catchNode, TypeReference catchType) {
      setCatchType(cfg.getBlock(catchNode).getNumber(), catchType);
    }

    public void setCatchType(int blockNumber, TypeReference catchType) {
      if (catchTypes.length <= blockNumber) {
        TypeReference[][] data = new TypeReference[blockNumber + 1][];
        System.arraycopy(catchTypes, 0, data, 0, catchTypes.length);
        catchTypes = data;
      }

      if (catchTypes[blockNumber] == null) {
        catchTypes[blockNumber] = new TypeReference[] { catchType };
      } else {
        TypeReference[] data = catchTypes[blockNumber];

        for (int i = 0; i < data.length; i++) {
          if (data[i] == catchType) {
            return;
          }
        }

        TypeReference[] newData = new TypeReference[data.length + 1];
        System.arraycopy(data, 0, newData, 0, data.length);
        newData[data.length] = catchType;

        catchTypes[blockNumber] = newData;
      }
    }

    public TypeReference[][] getCatchTypes() {
      return catchTypes;
    }
    
    public boolean hasValue(CAstNode n) {
      return results.containsKey(n);
    }

    public final int setValue(CAstNode n, int v) {
      results.put(n, new Integer(v));
      return v;
    }

    public final int getValue(CAstNode n) {
      if (results.containsKey(n))
        return results.get(n).intValue();
      else {
        if (DEBUG) {
          System.err.println(("no value for " + n.getKind()));
        }
        return -1;
      }
    }

  }

  private final class TypeContext extends EntityContext {

    private TypeContext(WalkContext parent, CAstEntity n) {
      super(parent, n);
    }

    public CAstControlFlowMap getControlFlow() {
      Assertions.UNREACHABLE("TypeContext.getControlFlow()");
      return null;
    }

    public IncipientCFG cfg() {
      Assertions.UNREACHABLE("TypeContext.cfg()");
      return null;
    }

    public UnwindState getUnwindState() {
      Assertions.UNREACHABLE("TypeContext.getUnwindState()");
      return null;
    }
  }

  private class LocalContext extends DelegatingContext {
    private final Scope localScope;

    LocalContext(WalkContext parent, Scope localScope) {
      super(parent);
      this.localScope = localScope;
      parent.entityScopes().add(localScope);
    }

    public Scope currentScope() {
      return localScope;
    }
  }

  /**
   * lexical access information for some entity scope. used during call graph
   * construction to handle lexical accesses.
   */
  public static class AstLexicalInformation implements LexicalInformation {
    /**
     * the name of this function, as it appears in the definer portion of a
     * lexical name
     */
    private final String functionLexicalName;

    /**
     * names possibly accessed in a nested lexical scope, represented as pairs
     * (name,nameOfDefiningEntity)
     */
    private final Pair[] exposedNames;

    /**
     * map from instruction index and exposed name (via its index in
     * {@link #exposedNames}) to the value number for the name at that
     * instruction index. This can vary at different instructions due to SSA
     * (and this information is updated during {@link SSAConversion}).
     */
    private final int[][] instructionLexicalUses;

    /**
     * maps each exposed name (via its index in {@link #exposedNames}) to its
     * value number at method exit.
     */
    private final int[] exitLexicalUses;

    /**
     * the names of the enclosing methods declaring names that are lexically
     * accessed by the entity
     */
    private final String[] scopingParents;

    /**
     * all value numbers appearing as entries in {@link #instructionLexicalUses}
     * and {@link #exitLexicalUses}, computed lazily
     */
    private MutableIntSet allExposedUses = null;

    /**
     * names of exposed variables of this method that cannot be written outside
     */
    private final Set readOnlyNames;

    @SuppressWarnings("unchecked")
    public AstLexicalInformation(AstLexicalInformation original) {
      this.functionLexicalName = original.functionLexicalName;

      if (original.exposedNames != null) {
        exposedNames = new Pair[original.exposedNames.length];
        for (int i = 0; i < exposedNames.length; i++) {
          exposedNames[i] = Pair.make(original.exposedNames[i].fst, original.exposedNames[i].snd);
        }
      } else {
        exposedNames = null;
      }

      instructionLexicalUses = new int[original.instructionLexicalUses.length][];
      for (int i = 0; i < instructionLexicalUses.length; i++) {
        int[] x = original.instructionLexicalUses[i];
        if (x != null) {
          instructionLexicalUses[i] = new int[x.length];
          for (int j = 0; j < x.length; j++) {
            instructionLexicalUses[i][j] = x[j];
          }
        }
      }

      if (original.exitLexicalUses != null) {
        exitLexicalUses = new int[original.exitLexicalUses.length];
        for (int i = 0; i < exitLexicalUses.length; i++) {
          exitLexicalUses[i] = original.exitLexicalUses[i];
        }
      } else {
        exitLexicalUses = null;
      }

      if (original.scopingParents != null) {
        scopingParents = new String[original.scopingParents.length];
        for (int i = 0; i < scopingParents.length; i++) {
          scopingParents[i] = original.scopingParents[i];
        }
      } else {
        scopingParents = null;
      }

      readOnlyNames = original.readOnlyNames;
    }

    private int[] buildLexicalUseArray(Pair, Integer>[] exposedNames, String entityName) {
      if (exposedNames != null) {
        int[] lexicalUses = new int[exposedNames.length];
        for (int j = 0; j < exposedNames.length; j++) {
          if (entityName == null || entityName.equals(exposedNames[j].fst.snd)) {
            lexicalUses[j] = exposedNames[j].snd;
          } else {
            lexicalUses[j] = -1;
          }
        }

        return lexicalUses;
      } else {
        return null;
      }
    }

    private Pair[] buildLexicalNamesArray(Pair, Integer>[] exposedNames) {
      if (exposedNames != null) {
        @SuppressWarnings("unchecked")
        Pair[] lexicalNames = new Pair[exposedNames.length];
        for (int j = 0; j < exposedNames.length; j++) {
          lexicalNames[j] = exposedNames[j].fst;
        }

        return lexicalNames;
      } else {
        return null;
      }
    }

    @SuppressWarnings("unchecked")
    AstLexicalInformation(String entityName, Scope scope, SSAInstruction[] instrs,
        Set, Integer>> exposedNamesForReadSet,
        Set, Integer>> exposedNamesForWriteSet, Set accesses) {
      this.functionLexicalName = entityName;

      Pair, Integer>[] EN = null;
      if (exposedNamesForReadSet != null || exposedNamesForWriteSet != null) {
        Set, Integer>> exposedNamesSet = new HashSet, Integer>>();
        if (exposedNamesForReadSet != null) {
          exposedNamesSet.addAll(exposedNamesForReadSet);
        }
        if (exposedNamesForWriteSet != null) {
          exposedNamesSet.addAll(exposedNamesForWriteSet);
        }
        EN = exposedNamesSet.toArray(new Pair[exposedNamesSet.size()]);
      }

      if (exposedNamesForReadSet != null) {
        Set readOnlyNames = new HashSet();
        for (Pair, Integer> v : exposedNamesForReadSet) {
          if (entityName != null && entityName.equals(v.fst.snd)) {
            readOnlyNames.add(v.fst.fst);
          }
        }
        if (exposedNamesForWriteSet != null) {
          for (Pair, Integer> v : exposedNamesForWriteSet) {
            if (entityName != null && entityName.equals(v.fst.snd)) {
              readOnlyNames.remove(v.fst.fst);
            }
          }
        }
        this.readOnlyNames = readOnlyNames;
      } else {
        this.readOnlyNames = null;
      }

      this.exposedNames = buildLexicalNamesArray(EN);

      // the value numbers stored in exitLexicalUses and instructionLexicalUses
      // are identical at first; they will be updated
      // as needed during the final SSA conversion
      this.exitLexicalUses = buildLexicalUseArray(EN, entityName);

      this.instructionLexicalUses = new int[instrs.length][];
      for (int i = 0; i < instrs.length; i++) {
        if (instrs[i] instanceof SSAAbstractInvokeInstruction) {
          this.instructionLexicalUses[i] = buildLexicalUseArray(EN, null);
        }
      }

      if (accesses != null) {
        Set parents = new LinkedHashSet();
        for (Iterator ACS = accesses.iterator(); ACS.hasNext();) {
          Access AC = ACS.next();
          if (AC.variableDefiner != null) {
            parents.add(AC.variableDefiner);
          }
        }
        scopingParents = parents.toArray(new String[parents.size()]);

        if (DEBUG_LEXICAL) {
          System.err.println(("scoping parents of " + scope.getEntity()));
          System.err.println(parents.toString());
        }

      } else {
        scopingParents = null;
      }

      if (DEBUG_NAMES) {
        System.err.println(("lexical uses of " + scope.getEntity()));
        for (int i = 0; i < instructionLexicalUses.length; i++) {
          if (instructionLexicalUses[i] != null) {
            System.err.println(("  lexical uses of " + instrs[i]));
            for (int j = 0; j < instructionLexicalUses[i].length; j++) {
              System.err.println(("    " + this.exposedNames[j].fst + ": " + instructionLexicalUses[i][j]));
            }
          }
        }
      }
    }

    public int[] getExitExposedUses() {
      return exitLexicalUses;
    }

    private static final int[] NONE = new int[0];

    public int[] getExposedUses(int instructionOffset) {
      return instructionLexicalUses[instructionOffset] == null ? NONE : instructionLexicalUses[instructionOffset];
    }

    public IntSet getAllExposedUses() {
      if (allExposedUses == null) {
        allExposedUses = IntSetUtil.make();
        if (exitLexicalUses != null) {
          for (int i = 0; i < exitLexicalUses.length; i++) {
            if (exitLexicalUses[i] > 0) {
              allExposedUses.add(exitLexicalUses[i]);
            }
          }
        }
        if (instructionLexicalUses != null) {
          for (int i = 0; i < instructionLexicalUses.length; i++) {
            if (instructionLexicalUses[i] != null) {
              for (int j = 0; j < instructionLexicalUses[i].length; j++) {
                if (instructionLexicalUses[i][j] > 0) {
                  allExposedUses.add(instructionLexicalUses[i][j]);
                }
              }
            }
          }
        }
      }

      return allExposedUses;
    }

    public Pair[] getExposedNames() {
      return exposedNames;
    }

    public String[] getScopingParents() {
      return scopingParents;
    }

    /**
     * reset cached info about value numbers that may have changed
     */
    public void handleAlteration() {
      allExposedUses = null;
    }

    public boolean isReadOnly(String name) {
      return readOnlyNames != null && readOnlyNames.contains(name);
    }

    public String getScopingName() {
      return functionLexicalName;
    }
  };
 
  /**
   * record that in entity e, the access is performed.
   * 
   * If {@link #useLocalValuesForLexicalVars()} is true, the access is performed
   * using a local variable. in
   * {@link #patchLexicalAccesses(SSAInstruction[], Set)}, this information is
   * used to update an instruction that performs all the accesses at the
   * beginning of the method and defines the locals.
   */
  private void addAccess(WalkContext context, CAstEntity e, Access access) {
    context.getAccesses(e).add(access);
  }

  /**
   * Record that a name assigned a value number in the scope of entity may be
   * accessed by a lexically nested scope, i.e., the name may be
   * exposed to lexically nested scopes. This information is needed
   * during call graph construction to properly model the data flow due to the
   * access in the nested scope.
   * 
   * @param entity
   *          an entity in whose scope name is assigned a value number
   * @param declaration
   *          the declaring entity for name (possibly an enclosing scope of
   *          entity, in the case where entity
   *          {@link #useLocalValuesForLexicalVars() accesses the name via a
   *          local})
   * @param name
   *          the accessed name
   * @param valueNumber
   *          the name's value number in the scope of entity
   */
  private void addExposedName(CAstEntity entity, CAstEntity declaration, String name, int valueNumber, boolean isWrite, WalkContext context) {
    Pair, Integer> newVal = Pair.make(Pair.make(name, context.getEntityName(declaration)), valueNumber);
    context.exposeNameSet(entity, isWrite).add(newVal);
  }

  private void setDefaultValue(SymbolTable symtab, int vn, Object value) {
    if (value == CAstSymbol.NULL_DEFAULT_VALUE) {
      symtab.setDefaultValue(vn, null);
    } else {
      symtab.setDefaultValue(vn, value);
    }
  }

  protected IUnaryOpInstruction.IOperator translateUnaryOpcode(CAstNode op) {
    if (op == CAstOperator.OP_BITNOT)
      return AstConstants.UnaryOp.BITNOT;
    else if (op == CAstOperator.OP_NOT)
      return IUnaryOpInstruction.Operator.NEG;
    else if (op == CAstOperator.OP_SUB)
      return AstConstants.UnaryOp.MINUS;
    else if (op == CAstOperator.OP_ADD)
      return AstConstants.UnaryOp.PLUS;
    else
      Assertions.UNREACHABLE("cannot translate " + CAstPrinter.print(op));
    return null;

  }

        if (DEBUG_NAMES) {
  protected IBinaryOpInstruction.IOperator translateBinaryOpcode(CAstNode op) {
    if (op == CAstOperator.OP_ADD)
      return BinaryOpInstruction.Operator.ADD;
    else if (op == CAstOperator.OP_DIV)
      return BinaryOpInstruction.Operator.DIV;
    else if (op == CAstOperator.OP_LSH)
      return ShiftInstruction.Operator.SHL;
    else if (op == CAstOperator.OP_MOD)
      return BinaryOpInstruction.Operator.REM;
    else if (op == CAstOperator.OP_MUL)
      return BinaryOpInstruction.Operator.MUL;
    else if (op == CAstOperator.OP_RSH)
      return ShiftInstruction.Operator.SHR;
    else if (op == CAstOperator.OP_SUB)
      return BinaryOpInstruction.Operator.SUB;
    else if (op == CAstOperator.OP_URSH)
      return ShiftInstruction.Operator.USHR;
    else if (op == CAstOperator.OP_BIT_AND)
      return BinaryOpInstruction.Operator.AND;
    else if (op == CAstOperator.OP_BIT_OR)
      return BinaryOpInstruction.Operator.OR;
    else if (op == CAstOperator.OP_BIT_XOR)
      return BinaryOpInstruction.Operator.XOR;
    else if (op == CAstOperator.OP_CONCAT)
      return AstConstants.BinaryOp.CONCAT;
    else if (op == CAstOperator.OP_EQ)
      return AstConstants.BinaryOp.EQ;
    else if (op == CAstOperator.OP_STRICT_EQ)
      return AstConstants.BinaryOp.STRICT_EQ;
    else if (op == CAstOperator.OP_GE)
      return AstConstants.BinaryOp.GE;
    else if (op == CAstOperator.OP_GT)
      return AstConstants.BinaryOp.GT;
    else if (op == CAstOperator.OP_LE)
      return AstConstants.BinaryOp.LE;
    else if (op == CAstOperator.OP_LT)
      return AstConstants.BinaryOp.LT;
    else if (op == CAstOperator.OP_NE)
      return AstConstants.BinaryOp.NE;
    else if (op == CAstOperator.OP_STRICT_NE)
      return AstConstants.BinaryOp.STRICT_NE;
    else {
      Assertions.UNREACHABLE("cannot translate " + CAstPrinter.print(op));
      return null;
    }
  }

  protected IConditionalBranchInstruction.IOperator translateConditionOpcode(CAstNode op) {
    if (op == CAstOperator.OP_EQ)
      return ConditionalBranchInstruction.Operator.EQ;
    else if (op == CAstOperator.OP_GE)
      return ConditionalBranchInstruction.Operator.GE;
    else if (op == CAstOperator.OP_GT)
      return ConditionalBranchInstruction.Operator.GT;
    else if (op == CAstOperator.OP_LE)
      return ConditionalBranchInstruction.Operator.LE;
    else if (op == CAstOperator.OP_LT)
      return ConditionalBranchInstruction.Operator.LT;
    else if (op == CAstOperator.OP_NE)
      return ConditionalBranchInstruction.Operator.NE;

    else {
      Assertions.UNREACHABLE("cannot translate " + CAstPrinter.print(op));
      return null;
    }
  }

  private String[] makeNameMap(CAstEntity n, Set scopes) {
    // all scopes share the same underlying symtab, which is what
    // size really refers to.
    String[] map = new String[scopes.iterator().next().size() + 1];

    if (DEBUG_NAMES) {
      System.err.println(("names array of size " + map.length));
    }

    for (Iterator S = scopes.iterator(); S.hasNext();) {
      Scope scope = S.next();
      for (Iterator I = scope.getAllNames(); I.hasNext();) {
        String nm = I.next();
        Symbol v = (Symbol) scope.lookup(nm);

        if (v.isInternalName()) {
          continue;
        }

        // constants can flow to multiple variables
        if (scope.isConstant(v.valueNumber()))
          continue;

        assert map[v.valueNumber()] == null || map[v.valueNumber()].equals(nm) : "value number " + v.valueNumber()
            + " mapped to multiple names in " + n.getName() + ": " + nm + " and " + map[v.valueNumber()];

        map[v.valueNumber()] = nm;

          System.err.println(("mapping name " + nm + " to " + v.valueNumber()));
        }
      }
    }

    return map;
  }

  protected final CAstType getTypeForNode(WalkContext context, CAstNode node) {
    if (context.top().getNodeTypeMap() != null) {
      return context.top().getNodeTypeMap().getNodeType(node);
    } else {
      return null;
    }
  }

  /**
   * find any AstLexicalAccess instructions in instrs with a zero access count,
   * and change them to perform specified accesses. If accesses is empty, null
   * out the pointers to the AstLexicalAccess instructions in the array.
   * 
   * Presumably, such empty AstLexicalAccess instructions should only exist if
   * {@link #useLocalValuesForLexicalVars()} returns true?
   */
  private void patchLexicalAccesses(SSAInstruction[] instrs, Set accesses) {
    Access[] AC = accesses == null || accesses.isEmpty() ? (Access[]) null : (Access[]) accesses.toArray(new Access[accesses.size()]);
    for (int i = 0; i < instrs.length; i++) {
      if (instrs[i] instanceof AstLexicalAccess && ((AstLexicalAccess) instrs[i]).getAccessCount() == 0) {
        // should just be AstLexicalRead for now; may add support for
        // AstLexicalWrite later
        assert instrs[i] instanceof AstLexicalRead;
        assert useLocalValuesForLexicalVars();
        if (AC != null) {
          ((AstLexicalAccess) instrs[i]).setAccesses(AC);
        } else {
          instrs[i] = null;
        }
      }
    }
  }

  private Position getPosition(CAstSourcePositionMap map, CAstNode n) {
    if (map.getPosition(n) != null) {
      return map.getPosition(n);
    } else {
      for (int i = 0; i < n.getChildCount(); i++) {
        Position p = getPosition(map, n.getChild(i));
        if (p != null) {
          return p;
        }
      }

      return null;
    }
  }

  protected WalkContext makeFileContext(WalkContext c, CAstEntity n) {
    return new FileContext((WalkContext) c, n.getName());
  }

  protected WalkContext makeTypeContext(WalkContext c, CAstEntity n) {
    return new TypeContext((WalkContext) c, n);
  }

  protected WalkContext makeCodeContext(WalkContext c, CAstEntity n) {
    WalkContext context = (WalkContext) c;
    AbstractScope scope;
    if (n.getKind() == CAstEntity.SCRIPT_ENTITY)
      scope = makeScriptScope(n, context.currentScope());
    else
      scope = makeFunctionScope(n, context.currentScope());
    return new CodeEntityContext(context, scope, n);
  }

  protected boolean enterEntity(final CAstEntity n, WalkContext context, CAstVisitor visitor) {
    if (DEBUG_TOP)
      System.err.println(("translating " + n.getName()));
    return false;
  }

  protected boolean visitFileEntity(CAstEntity n, WalkContext context, WalkContext fileContext, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFileEntity(CAstEntity n, WalkContext context, WalkContext fileContext, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitFieldEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFieldEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) {
    // Define a new field in the enclosing type, if the language we're
    // processing allows such.
    CAstEntity topEntity = context.top(); // better be a type
    assert topEntity.getKind() == CAstEntity.TYPE_ENTITY : "Parent of field entity is not a type???";
    defineField(topEntity, (WalkContext) context, n);
  }

  protected boolean visitGlobalEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveGlobalEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) {
    // Define a new field in the enclosing type, if the language we're
    // processing allows such.
    context.getGlobalScope().declare(new CAstSymbolImpl(n.getName()));
  }

  protected boolean visitTypeEntity(CAstEntity n, WalkContext context, WalkContext typeContext, CAstVisitor visitor) {
    return !defineType(n, (WalkContext) context);
  }

  protected void leaveTypeEntity(CAstEntity n, WalkContext context, WalkContext typeContext, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitFunctionEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    if (n.getAST() == null) // presumably abstract
      declareFunction(n, (WalkContext) context);
    else
      initFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
    return false;
  }

  protected void leaveFunctionEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    if (n.getAST() != null) // non-abstract
      closeFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
  }

  protected boolean visitMacroEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    return true;
  }

  protected boolean visitScriptEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    declareFunction(n, (WalkContext) codeContext);
    initFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
    return false;
  }

  protected void leaveScriptEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    closeFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
  }

  public void initFunctionEntity(final CAstEntity n, WalkContext parentContext, WalkContext functionContext) {
    // entry block
  }
    functionContext.cfg().makeEntryBlock(functionContext.cfg().newBlock(false));
    // first real block
    functionContext.cfg().newBlock(true);
    // prologue code, if any
    doPrologue(functionContext);
  }

  public void closeFunctionEntity(final CAstEntity n, WalkContext parentContext, WalkContext functionContext) {
    // exit block
    functionContext.cfg().makeExitBlock(functionContext.cfg().newBlock(true));

    // create code entry stuff for this entity
    SymbolTable symtab = ((AbstractScope) functionContext.currentScope()).getUnderlyingSymtab();
    TypeReference[][] catchTypes = functionContext.getCatchTypes();
    AstCFG cfg = new AstCFG(n, functionContext.cfg(), symtab);
    Position[] line = functionContext.cfg().getLinePositionMap();
    boolean katch = functionContext.cfg().hasCatchBlock();
    boolean monitor = functionContext.cfg().hasMonitorOp();
    String[] nms = makeNameMap(n, functionContext.entityScopes());

    /*
     * Set reachableBlocks = DFS.getReachableNodes(cfg,
     * Collections.singleton(cfg.entry()));
     * Assertions._assert(reachableBlocks.size() == cfg.getNumberOfNodes(),
     * cfg.toString());
     */

    // (put here to allow subclasses to handle stuff in scoped entities)
    // assemble lexical information
    patchLexicalAccesses(cfg.getInstructions(), functionContext.getAccesses(n));
    AstLexicalInformation LI = new AstLexicalInformation(functionContext.getEntityName(n), (AbstractScope) functionContext.currentScope(), cfg.getInstructions(),
        functionContext.exposeNameSet(n, false), 
        functionContext.exposeNameSet(n, true), 
        functionContext.getAccesses(n));

    DebuggingInformation DBG = new AstDebuggingInformation(n.getPosition(), line, nms);

    // actually make code body
    defineFunction(n, parentContext, cfg, symtab, katch, catchTypes, monitor, LI, DBG);
  }

  protected WalkContext makeLocalContext(WalkContext context, CAstNode n) {
    return new LocalContext((WalkContext) context, makeLocalScope(n, ((WalkContext) context).currentScope()));
  }

  protected WalkContext makeUnwindContext(WalkContext context, CAstNode n, CAstVisitor visitor) {
    // here, n represents the "finally" block of the unwind
    return new UnwindContext(n, (WalkContext) context, visitor);
  }

  private Map> entity2ExposedNames;
  protected int processFunctionExpr(CAstNode n, WalkContext context) {
    CAstEntity fn = (CAstEntity) n.getChild(0).getValue();
    declareFunction(fn, context);
    int result = context.currentScope().allocateTempValue();
    int ex = context.currentScope().allocateTempValue();
    doMaterializeFunction(n, context, result, ex, fn);
    return result;
  }

  protected boolean visitFunctionExpr(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFunctionExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    int result = processFunctionExpr(n, c);
    c.setValue(n, result);
  }

  protected boolean visitFunctionStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFunctionStmt(CAstNode n, WalkContext context, CAstVisitor visitor) {
    int result = processFunctionExpr(n, context);
    CAstEntity fn = (CAstEntity) n.getChild(0).getValue();
    // FIXME: handle redefinitions of functions
    Scope cs = context.currentScope();
    if (cs.contains(fn.getName()) && !cs.isLexicallyScoped(cs.lookup(fn.getName())) && !cs.isGlobal(cs.lookup(fn.getName()))) {
      // if we already have a local with the function's name, write the function
      // value to that local
      assignValue(n, context, cs.lookup(fn.getName()), fn.getName(), result);
    } else if (topLevelFunctionsInGlobalScope() && context.top().getKind() == CAstEntity.SCRIPT_ENTITY) {
      context.getGlobalScope().declare(new FinalCAstSymbol(fn.getName()));
      assignValue(n, context, cs.lookup(fn.getName()), fn.getName(), result);
      context.currentScope().declare(new FinalCAstSymbol(fn.getName()), result);
    }
  }

  protected boolean visitLocalScope(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveLocalScope(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(0)));
  }

  protected boolean visitBlockExpr(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(n.getChildCount() - 1)));
  }

  protected boolean visitBlockStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitLoop(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    // loop test block
    context.cfg().newBlock(true);
    PreBasicBlock headerB = context.cfg().getCurrentBlock();
    visitor.visit(n.getChild(0), context, visitor);

    assert c.getValue(n.getChild(0)) != -1 : "error in loop test " + CAstPrinter.print(n.getChild(0), context.top().getSourceMap())
        + " of loop " + CAstPrinter.print(n, context.top().getSourceMap());
    context.cfg().addInstruction(
        insts.ConditionalBranchInstruction(context.cfg().currentInstruction, translateConditionOpcode(CAstOperator.OP_EQ), null, c.getValue(n.getChild(0)), context
            .currentScope().getConstantValue(new Integer(0))));
    PreBasicBlock branchB = context.cfg().getCurrentBlock();

    // loop body
    context.cfg().newBlock(true);
    visitor.visit(n.getChild(1), context, visitor);
    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
      context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
      PreBasicBlock bodyB = context.cfg().getCurrentBlock();
      context.cfg().addEdge(bodyB, headerB);

      // next block
      context.cfg().newBlock(false);
    }

    PreBasicBlock nextB = context.cfg().getCurrentBlock();

    // control flow mapping;
    context.cfg().addEdge(branchB, nextB);
    return true;
  }

  // Make final to prevent overriding
  protected final void leaveLoopHeader(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveLoop(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitGetCaughtException(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveGetCaughtException(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    String nm = (String) n.getChild(0).getValue();
    context.currentScope().declare(new FinalCAstSymbol(nm));
    context.cfg().addInstruction(
        insts.GetCaughtExceptionInstruction(context.cfg().currentInstruction, context.cfg().getCurrentBlock().getNumber(), context.currentScope()
            .lookup(nm).valueNumber()));
  }

  protected boolean visitThis(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveThis(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, 1);
  }

  protected boolean visitSuper(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }


  protected void leaveSuper(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, 1);
  }

  protected boolean visitCall(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveCall(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    int exp = context.currentScope().allocateTempValue();
    int fun = c.getValue(n.getChild(0));
    CAstNode functionName = n.getChild(1);
    int[] args = new int[n.getChildCount() - 2];
    for (int i = 0; i < args.length; i++) {
      args[i] = c.getValue(n.getChild(i + 2));
    }
    doCall(context, n, result, exp, functionName, fun, args);
  }

  protected boolean visitVar(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveVar(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    String nm = (String) n.getChild(0).getValue();
    assert nm != null : "cannot find var for " + CAstPrinter.print(n, context.getSourceMap());
    Symbol s = context.currentScope().lookup(nm);
    assert s != null : "cannot find symbol for " + nm + " at " + CAstPrinter.print(n, context.getSourceMap());
    if (context.currentScope().isGlobal(s)) {
      c.setValue(n, doGlobalRead(n, context, nm));
    } else if (context.currentScope().isLexicallyScoped(s)) {
      c.setValue(n, doLexicallyScopedRead(n, context, nm));
    } else {
      c.setValue(n, doLocalRead(context, nm));
    }
  }

  protected boolean visitConstant(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveConstant(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    c.setValue(n, context.currentScope().getConstantValue(n.getValue()));
  }

  protected boolean visitBinaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  private boolean handleBinaryOpThrow(CAstNode n, CAstNode op, WalkContext context) {
    // currently, only integer / and % throw exceptions
    boolean mayBeInteger = false;
    Collection labels = context.getControlFlow().getTargetLabels(n);
    if (!labels.isEmpty()) {
      context.cfg().addPreNode(n, context.getUnwindState());

      mayBeInteger = true;
      assert op == CAstOperator.OP_DIV || op == CAstOperator.OP_MOD : CAstPrinter.print(n);
      for (Iterator iter = labels.iterator(); iter.hasNext();) {
        Object label = iter.next();
        CAstNode target = context.getControlFlow().getTarget(n, label);
        if (target == CAstControlFlowMap.EXCEPTION_TO_EXIT)
          context.cfg().addPreEdgeToExit(n, true);
        else
          context.cfg().addPreEdge(n, target, true);
      }
    }

    return mayBeInteger;
  }

  protected void leaveBinaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    CAstNode l = n.getChild(1);
    CAstNode r = n.getChild(2);
    assert c.getValue(r) != -1 : CAstPrinter.print(n);
    assert c.getValue(l) != -1 : CAstPrinter.print(n);

    boolean mayBeInteger = handleBinaryOpThrow(n, n.getChild(0), context);

    context.cfg().addInstruction(
    return false;
        insts.BinaryOpInstruction(context.cfg().currentInstruction, translateBinaryOpcode(n.getChild(0)), false, false, result, c.getValue(l), c.getValue(r),
            mayBeInteger));

    if (mayBeInteger) {
      context.cfg().newBlock(true);
    }
  }

  protected boolean visitUnaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveUnaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    CAstNode v = n.getChild(1);
    context.cfg().addInstruction(insts.UnaryOpInstruction(context.cfg().currentInstruction, translateUnaryOpcode(n.getChild(0)), result, c.getValue(v)));
  }

  protected boolean visitArrayLength(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveArrayLength(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    int arrayValue = c.getValue(n.getChild(0));
    context.cfg().addInstruction(insts.ArrayLengthInstruction(context.cfg().currentInstruction, result, arrayValue));
  }

  protected boolean visitArrayRef(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveArrayRef(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int arrayValue = c.getValue(n.getChild(0));
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    arrayOpHandler.doArrayRead(context, result, arrayValue, n, gatherArrayDims(c, n));
  }

  protected boolean visitDeclStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  // TODO: should we handle exploded declaration nodes here instead?
  protected void leaveDeclStmt(CAstNode n, WalkContext c, CAstVisitor visitor) {
    CAstSymbol s = (CAstSymbol) n.getChild(0).getValue();
    String nm = s.name();
    Scope scope = c.currentScope();
    if (n.getChildCount() == 2) {
      CAstNode v = n.getChild(1);
      if (scope.contains(nm) && scope.lookup(nm).getDefiningScope() == scope) {
        assert !s.isFinal();
        doLocalWrite(c, nm, c.getValue(v));
      } else if (v.getKind() != CAstNode.CONSTANT && v.getKind() != CAstNode.VAR && v.getKind() != CAstNode.THIS) {
        scope.declare(s, c.getValue(v));
      } else {
        scope.declare(s);
        doLocalWrite(c, nm, c.getValue(v));
      }
    } else {
      c.currentScope().declare(s);
    }
  }

  protected boolean visitReturn(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveReturn(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (n.getChildCount() > 0) {
      context.cfg().addInstruction(insts.ReturnInstruction(c.cfg().currentInstruction, c.getValue(n.getChild(0)), false));
    } else {
      context.cfg().addInstruction(insts.ReturnInstruction(context.cfg().currentInstruction));
    }

    context.cfg().addPreNode(n, context.getUnwindState());
    context.cfg().newBlock(false);
    context.cfg().addPreEdgeToExit(n, false);
  }

  protected boolean visitIfgoto(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  protected void leaveIfgoto(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (n.getChildCount() == 1) {
      context.cfg().addInstruction(
          insts.ConditionalBranchInstruction(c.cfg().currentInstruction, translateConditionOpcode(CAstOperator.OP_NE), null, c.getValue(n.getChild(0)), context
              .currentScope().getConstantValue(new Integer(0))));
    } else if (n.getChildCount() == 3) {
      context.cfg().addInstruction(
          insts.ConditionalBranchInstruction(c.cfg().currentInstruction, translateConditionOpcode(n.getChild(0)), null, c.getValue(n.getChild(1)),
              c.getValue(n.getChild(2))));
    } else {
      Assertions.UNREACHABLE();
    }

    context.cfg().addPreNode(n, context.getUnwindState());
    context.cfg().newBlock(true);
    context.cfg().addPreEdge(n, context.getControlFlow().getTarget(n, Boolean.TRUE), false);
  }

  protected boolean visitGoto(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveGoto(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
      context.cfg().addPreNode(n, context.getUnwindState());
      context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
      context.cfg().newBlock(false);
      if (context.getControlFlow().getTarget(n, null) == null) {
        assert context.getControlFlow().getTarget(n, null) != null : context.getControlFlow() + " does not map " + n + " ("
            + context.getSourceMap().getPosition(n) + ")";
      }

      context.cfg().addPreEdge(n, context.getControlFlow().getTarget(n, null), false);
    }
  }

  protected boolean visitLabelStmt(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (!context.getControlFlow().getSourceNodes(n).isEmpty()) {
      context.cfg().newBlock(true);
      context.cfg().addPreNode(n, context.getUnwindState());
    }
    return false;
  }

  protected void leaveLabelStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected void processIf(CAstNode n, boolean isExpr, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    PreBasicBlock trueB = null, falseB = null;
    // conditional
    CAstNode l = n.getChild(0);
    visitor.visit(l, context, visitor);
    context.cfg().addInstruction(
        insts.ConditionalBranchInstruction(c.cfg().currentInstruction, translateConditionOpcode(CAstOperator.OP_EQ), null, c.getValue(l), context.currentScope()
            .getConstantValue(new Integer(0))));
    PreBasicBlock srcB = context.cfg().getCurrentBlock();
    // true clause
    context.cfg().newBlock(true);
    CAstNode r = n.getChild(1);
    visitor.visit(r, context, visitor);
    if (isExpr)
      context.cfg().addInstruction(new AssignInstruction(c.cfg().currentInstruction, c.getValue(n), c.getValue(r)));
    if (n.getChildCount() == 3) {
      if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
        context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
        trueB = context.cfg().getCurrentBlock();

        // false clause
        context.cfg().newBlock(false);
      }

      falseB = context.cfg().getCurrentBlock();
      CAstNode f = n.getChild(2);
      visitor.visit(f, context, visitor);
      if (isExpr)
        context.cfg().addInstruction(new AssignInstruction(context.cfg().currentInstruction, c.getValue(n), c.getValue(f)));
    }

    // end
    context.cfg().newBlock(true);
    if (n.getChildCount() == 3) {
      if (trueB != null)
        context.cfg().addEdge(trueB, context.cfg().getCurrentBlock());
  }
      context.cfg().addEdge(srcB, falseB);
    } else {
      context.cfg().addEdge(srcB, context.cfg().getCurrentBlock());
    }
  }

  // Make final to prevent overriding
  protected final void leaveIfStmtCondition(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfStmtTrueClause(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfExprCondition(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfExprTrueClause(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfExpr(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitIfStmt(CAstNode n, WalkContext c, CAstVisitor visitor) {
    processIf(n, false, c, visitor);
    return true;
  }

  protected boolean visitIfExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    processIf(n, true, c, visitor);
    return true;
  }

  protected boolean visitNew(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }
  protected void leaveNew(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;

    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);

    int[] arguments;
    if (n.getChildCount() <= 1) {
      arguments = null;
    } else {
      arguments = new int[n.getChildCount() - 1];
      for (int i = 1; i < n.getChildCount(); i++) {
        arguments[i - 1] = c.getValue(n.getChild(i));
      }
    }
    doNewObject(context, n, result, n.getChild(0).getValue(), arguments);
  }

  protected boolean visitObjectLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveObjectLiteralFieldInit(CAstNode n, int i, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (n.getChild(i).getKind() == CAstNode.EMPTY) {
      handleUnspecifiedLiteralKey(context, n, i, visitor);
    }
    doFieldWrite(context, c.getValue(n.getChild(0)), n.getChild(i), n, c.getValue(n.getChild(i + 1)));
  }

  protected void leaveObjectLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(0)));
  }

  protected boolean visitArrayLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveArrayLiteralObject(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(0)));
  }

  protected void leaveArrayLiteralInitElement(CAstNode n, int i, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    arrayOpHandler.doArrayWrite(context, c.getValue(n.getChild(0)), n,
        new int[] { context.currentScope().getConstantValue(new Integer(i - 1)) }, c.getValue(n.getChild(i)));
  }

  protected void leaveArrayLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitObjectRef(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveObjectRef(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    CAstNode elt = n.getChild(1);
    doFieldRead(context, result, c.getValue(n.getChild(0)), elt, n);
  }

  public boolean visitAssign(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  public void leaveAssign(CAstNode n, WalkContext c, CAstVisitor visitor) {
    if (n.getKind() == CAstNode.ASSIGN) {
      c.setValue(n, c.getValue(n.getChild(1)));
    } else {
      c.setValue(n, c.getValue(n.getChild(0)));
    }
  }

  private int[] gatherArrayDims(WalkContext c, CAstNode n) {
    int numDims = n.getChildCount() - 2;
    int[] dims = new int[numDims];
    for (int i = 0; i < numDims; i++)
      dims[i] = c.getValue(n.getChild(i + 2));
    return dims;
  }

  /* Prereq: a.getKind() == ASSIGN_PRE_OP || a.getKind() == ASSIGN_POST_OP */
  protected int processAssignOp(CAstNode n, CAstNode v, CAstNode a, int temp, boolean post, WalkContext c) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    CAstNode op = a.getChild(2);
    int temp2 = context.currentScope().allocateTempValue();

    boolean mayBeInteger = handleBinaryOpThrow(a, op, context);

    } else {
    context.cfg().addInstruction(
        insts.BinaryOpInstruction(context.cfg().currentInstruction, translateBinaryOpcode(op), false, false, temp2, temp, rval, mayBeInteger));

    if (mayBeInteger) {
      context.cfg().newBlock(true);
    }

    return temp2;
  }

  protected boolean visitArrayRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveArrayRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    c.setValue(n, rval);
    arrayOpHandler.doArrayWrite(context, c.getValue(n.getChild(0)), n, gatherArrayDims(c, n), rval);
  }

  protected boolean visitArrayRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveArrayRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int temp = context.currentScope().allocateTempValue();
    int[] dims = gatherArrayDims(c, n);
    arrayOpHandler.doArrayRead(context, temp, c.getValue(n.getChild(0)), n, dims);
    int rval = processAssignOp(n, v, a, temp, !pre, c);
    c.setValue(n, pre ? rval : temp);
    arrayOpHandler.doArrayWrite(context, c.getValue(n.getChild(0)), n, dims, rval);
  }

  protected boolean visitObjectRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveObjectRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    c.setValue(n, rval);
    doFieldWrite(context, c.getValue(n.getChild(0)), n.getChild(1), n, rval);
  }

  protected void processObjectRefAssignOp(CAstNode n, CAstNode v, CAstNode a, WalkContext c) {
  }

  protected boolean visitObjectRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */

  protected void leaveObjectRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int temp = context.currentScope().allocateTempValue();
    doFieldRead(context, temp, c.getValue(n.getChild(0)), n.getChild(1), n);
    int rval = processAssignOp(n, v, a, temp, !pre, c);
    c.setValue(n, pre ? rval : temp);
    doFieldWrite(context, c.getValue(n.getChild(0)), n.getChild(1), n, rval);
  }

  protected boolean visitBlockExprAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockExprAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(n.getChildCount() - 1)));
  }

  protected boolean visitBlockExprAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockExprAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    c.setValue(n, c.getValue(n.getChild(n.getChildCount() - 1)));
  }

  protected boolean visitVarAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  /**
   * assign rval to nm as appropriate, depending on the scope of ls
   */
  protected void assignValue(CAstNode n, WalkContext context, Symbol ls, String nm, int rval) {
    if (context.currentScope().isGlobal(ls))
      }
      doGlobalWrite(context, nm, rval);
    else if (context.currentScope().isLexicallyScoped(ls)) {
      doLexicallyScopedWrite(context, nm, rval);
    } else {
      assert rval != -1 : CAstPrinter.print(n, context.top().getSourceMap());
      doLocalWrite(context, nm, rval);
    }
  }

  protected void leaveVarAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    String nm = (String) n.getChild(0).getValue();
    Symbol ls = context.currentScope().lookup(nm);
    c.setValue(n, rval);
    assignValue(n, context, ls, nm, rval);
  }

  protected boolean visitVarAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveVarAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    String nm = (String) n.getChild(0).getValue();
    Symbol ls = context.currentScope().lookup(nm);
    int temp;

    if (context.currentScope().isGlobal(ls))
      temp = doGlobalRead(n, context, nm);
    else if (context.currentScope().isLexicallyScoped(ls)) {
      temp = doLexicallyScopedRead(n, context, nm);
    } else {
      temp = doLocalRead(context, nm);
    }

    if (!pre) {
      int ret = context.currentScope().allocateTempValue();
      context.cfg().addInstruction(new AssignInstruction(c.cfg().currentInstruction, ret, temp));
      c.setValue(n, ret);
    }

    int rval = processAssignOp(n, v, a, temp, !pre, c);

    if (pre) {
      c.setValue(n, rval);
    }

    if (context.currentScope().isGlobal(ls)) {
      doGlobalWrite(context, nm, rval);
    } else if (context.currentScope().isLexicallyScoped(ls)) {
      doLexicallyScopedWrite(context, nm, rval);
    } else {
      doLocalWrite(context, nm, rval);
    }
  }

  }
  private boolean isSimpleSwitch(CAstNode n, WalkContext context, CAstVisitor visitor) {
    CAstControlFlowMap ctrl = context.getControlFlow();
    Collection caseLabels = ctrl.getTargetLabels(n);
    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();

      if (x == CAstControlFlowMap.SWITCH_DEFAULT)
        continue;

      CAstNode xn = (CAstNode) x;
      if (xn.getKind() == CAstNode.CONSTANT) {
        visitor.visit(xn, context, visitor);
        if (context.getValue(xn) != -1) {
          if (context.currentScope().isConstant(context.getValue(xn))) {
            Object val = context.currentScope().getConstantObject(context.getValue(xn));
            if (val instanceof Number) {
              Number num = (Number) val;
              if ((double) num.intValue() == num.doubleValue()) {
                continue;
              }
            }
          }
        }
      }

      return false;
    }

    return true;
  }

  private void doSimpleSwitch(CAstNode n, WalkContext context, CAstVisitor visitor) {
    PreBasicBlock defaultHackBlock = null;
    CAstControlFlowMap ctrl = context.getControlFlow();

    CAstNode switchValue = n.getChild(0);
    visitor.visit(switchValue, context, visitor);
    int v = context.getValue(switchValue);

    boolean hasExplicitDefault = ctrl.getTarget(n, CAstControlFlowMap.SWITCH_DEFAULT) != null;

    Collection caseLabels = ctrl.getTargetLabels(n);
    int cases = caseLabels.size();
    if (hasExplicitDefault)
      cases--;
    int[] casesAndLabels = new int[cases * 2];

    int defaultBlock = context.cfg().getCurrentBlock().getGraphNodeId() + 1;

    context.cfg().addInstruction(insts.SwitchInstruction(context.cfg().currentInstruction, v, defaultBlock, casesAndLabels));
    context.cfg().addPreNode(n, context.getUnwindState());
    // PreBasicBlock switchB = context.cfg().getCurrentBlock();
    context.cfg().newBlock(true);

    context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
    defaultHackBlock = context.cfg().getCurrentBlock();
    context.cfg().newBlock(false);

    CAstNode switchBody = n.getChild(1);
    visitor.visit(switchBody, context, visitor);
    context.cfg().newBlock(true);

    if (!hasExplicitDefault) {
      context.cfg().addEdge(defaultHackBlock, context.cfg().getCurrentBlock());
    }

    int cn = 0;
    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();
      CAstNode target = ctrl.getTarget(n, x);
      if (x == CAstControlFlowMap.SWITCH_DEFAULT) {
        context.cfg().addEdge(defaultHackBlock, context.cfg().getBlock(target));
      } else {
        Number caseLabel = (Number) context.currentScope().getConstantObject(context.getValue((CAstNode) x));
        casesAndLabels[2 * cn] = caseLabel.intValue();
        casesAndLabels[2 * cn + 1] = context.cfg().getBlock(target).getGraphNodeId();
        cn++;

        context.cfg().addPreEdge(n, target, false);
      }
    }
  }

  private void doIfConvertSwitch(CAstNode n, WalkContext context, CAstVisitor visitor) {
    CAstControlFlowMap ctrl = context.getControlFlow();
    context.cfg().addPreNode(n, context.getUnwindState());

    CAstNode switchValue = n.getChild(0);
    visitor.visit(switchValue, context, visitor);
    int v = context.getValue(switchValue);

    Collection caseLabels = ctrl.getTargetLabels(n);
    Map labelToBlock = new LinkedHashMap();
    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();
      if (x != CAstControlFlowMap.SWITCH_DEFAULT) {
        visitor.visit((CAstNode) x, context, visitor);
        context.cfg().addInstruction(
            insts.ConditionalBranchInstruction(context.cfg().currentInstruction, translateConditionOpcode(CAstOperator.OP_EQ), null, v, context.getValue((CAstNode) x)));
        labelToBlock.put(x, context.cfg().getCurrentBlock());
        context.cfg().newBlock(true);
      }
    }

    PreBasicBlock defaultGotoBlock = context.cfg().getCurrentBlock();
    context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
    context.cfg().newBlock(false);

    CAstNode switchBody = n.getChild(1);
    visitor.visit(switchBody, context, visitor);
    context.cfg().newBlock(true);

    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();
      if (x != CAstControlFlowMap.SWITCH_DEFAULT) {
        CAstNode target = ctrl.getTarget(n, x);
        context.cfg().addEdge(labelToBlock.get(x), context.cfg().getBlock(target));
      }
    }

    if (ctrl.getTarget(n, CAstControlFlowMap.SWITCH_DEFAULT) == null) {
      context.cfg().addEdge(defaultGotoBlock, context.cfg().getCurrentBlock());
    } else {
      CAstNode target = ctrl.getTarget(n, CAstControlFlowMap.SWITCH_DEFAULT);
      context.cfg().addEdge(defaultGotoBlock, context.cfg().getBlock(target));
    }
  }

  protected boolean visitSwitch(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (isSimpleSwitch(n, context, visitor)) {
      doSimpleSwitch(n, context, visitor);
      doIfConvertSwitch(n, context, visitor);
    }
    return true;
  }

  // Make final to prevent overriding
  protected final void leaveSwitchValue(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveSwitch(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitThrow(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveThrow(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    doThrow(context, c.getValue(n.getChild(0)));

    context.cfg().addPreNode(n, context.getUnwindState());
    context.cfg().newBlock(false);

    Collection labels = context.getControlFlow().getTargetLabels(n);
    for (Iterator iter = labels.iterator(); iter.hasNext();) {
      Object label = iter.next();
      CAstNode target = context.getControlFlow().getTarget(n, label);
      if (target == CAstControlFlowMap.EXCEPTION_TO_EXIT)
        context.cfg().addPreEdgeToExit(n, true);
      else
        context.cfg().addPreEdge(n, target, true);
    }
  }

  protected boolean visitCatch(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;

    // unreachable catch block
    if (context.getControlFlow().getSourceNodes(n).isEmpty()) {
      return true;
    }

    String id = (String) n.getChild(0).getValue();
    context.cfg().setCurrentBlockAsHandler();
    if (!context.currentScope().contains(id)) {
      context.currentScope().declare(new FinalCAstSymbol(id));
    }
    context.cfg().addInstruction(
        insts.GetCaughtExceptionInstruction(context.cfg().currentInstruction, context.cfg().getCurrentBlock().getNumber(), context.currentScope()
            .lookup(id).valueNumber()));

    context.cfg().addPreNode(n, context.getUnwindState());

    CAstType caughtType = getTypeForNode(context, n);
    if (caughtType != null) {
      TypeReference caughtRef = makeType(caughtType);
      context.setCatchType(n, caughtRef);
    } else {
      context.setCatchType(n, defaultCatchType());
    }

    return false;
  }

  protected void leaveCatch(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitUnwind(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveUnwind(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  private boolean hasIncomingEdges(CAstNode n, WalkContext context) {
    if (context.cfg().hasDelayedEdges(n)) {
      return true;
    } else {
      for (int i = 0; i < n.getChildCount(); i++) {
        if (hasIncomingEdges(n.getChild(i), context)) {
          return true;
        }
      }

      return false;
    }
  }

  protected boolean visitTry(final CAstNode n, WalkContext c, CAstVisitor visitor) {
    final WalkContext context = (WalkContext) c;
    boolean addSkipCatchGoto = false;
    visitor.visit(n.getChild(0), context, visitor);
    PreBasicBlock endOfTry = context.cfg().getCurrentBlock();

    if (!hasIncomingEdges(n.getChild(1), context)) {
      if (loader instanceof CAstAbstractLoader) {
        ((CAstAbstractLoader) loader).addMessage(context.getModule(), new Warning(Warning.MILD) {
          @Override
          public String getMsg() {
            return "Dead catch block at " + getPosition(context.getSourceMap(), n.getChild(1));
          }
        });
      }
      return true;
    }

    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
      addSkipCatchGoto = true;
      ;
      context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
      context.cfg().newBlock(false);
    }

    context.cfg().noteCatchBlock();
    visitor.visit(n.getChild(1), context, visitor);

    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
      context.cfg().newBlock(true);
    }

    if (addSkipCatchGoto) {
      PreBasicBlock afterBlock = context.cfg().getCurrentBlock();
      context.cfg().addEdge(endOfTry, afterBlock);
    }
    return true;
  }

  // Make final to prevent overriding
  protected final void leaveTryBlock(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveTry(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitEmpty(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveEmpty(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    c.setValue(n, context.currentScope().getConstantValue(null));
  }

  protected boolean visitPrimitive(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leavePrimitive(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);

    doPrimitive(result, context, n);
  }


    }
  protected boolean visitVoid(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveVoid(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, -1);
  }

  protected boolean visitAssert(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveAssert(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    boolean fromSpec = true;
    int result = c.getValue(n.getChild(0));
    if (n.getChildCount() == 2) {
      assert n.getChild(1).getKind() == CAstNode.CONSTANT;
      assert n.getChild(1).getValue() instanceof Boolean;
      fromSpec = n.getChild(1).getValue().equals(Boolean.TRUE);
    }
    context.cfg().addInstruction(new AstAssertInstruction(context.cfg().currentInstruction, result, fromSpec));
  }

  protected boolean visitEachElementGet(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  protected void leaveEachElementGet(CAstNode n, WalkContext c, CAstVisitor visitor) {
    int result = ((WalkContext) c).currentScope().allocateTempValue();
    c.setValue(n, result);
    ((WalkContext) c).cfg().addInstruction(new EachElementGetInstruction(c.cfg().currentInstruction, result, c.getValue(n.getChild(0))));
  }

  protected boolean visitEachElementHasNext(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  @Override
  protected void leaveEachElementHasNext(CAstNode n, WalkContext c, CAstVisitor visitor) {
    int result = ((WalkContext) c).currentScope().allocateTempValue();
    c.setValue(n, result);
    ((WalkContext) c).cfg().addInstruction(new EachElementHasNextInstruction(c.cfg().currentInstruction, result, c.getValue(n.getChild(0))));
  }

  protected boolean visitTypeLiteralExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  protected void leaveTypeLiteralExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;
    assert n.getChild(0).getKind() == CAstNode.CONSTANT;
    String typeNameStr = (String) n.getChild(0).getValue();
    TypeName typeName = TypeName.string2TypeName(typeNameStr);
    TypeReference typeRef = TypeReference.findOrCreate(loader.getReference(), typeName);

    int result = wc.currentScope().allocateTempValue();
    c.setValue(n, result);

    wc.cfg().addInstruction(insts.LoadMetadataInstruction(wc.cfg().currentInstruction, result, loader.getLanguage().getConstantType(typeRef), typeRef));
  }

  protected boolean visitIsDefinedExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  protected void leaveIsDefinedExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;
    int ref = c.getValue(n.getChild(0));
    int result = wc.currentScope().allocateTempValue();
    c.setValue(n, result);
    if (n.getChildCount() == 1) {
      wc.cfg().addInstruction(new AstIsDefinedInstruction(wc.cfg().currentInstruction, result, ref));
    } else {
      doIsFieldDefined(wc, result, ref, n.getChild(1));
    }
  }

  protected boolean visitEcho(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  protected void leaveEcho(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;

    int rvals[] = new int[n.getChildCount()];
    for (int i = 0; i < n.getChildCount(); i++) {
      rvals[i] = c.getValue(n.getChild(i));
    }

    wc.cfg().addInstruction(new AstEchoInstruction(wc.cfg().currentInstruction, rvals));
  public CAstEntity getIncludedEntity(CAstNode n) {
    if (n.getChild(0).getKind() == CAstNode.NAMED_ENTITY_REF) {
      assert namedEntityResolver != null;
      return (CAstEntity) namedEntityResolver.get(n.getChild(0).getChild(0).getValue());
    } else {
      return (CAstEntity) n.getChild(0).getValue();
    }
  }

  protected void leaveInclude(final CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;

    CAstEntity included = getIncludedEntity(n);

    if (included == null) {
      System.err.println(("cannot find include for " + CAstPrinter.print(n)));
      System.err.println(("from:\n" + namedEntityResolver));
    } else {
      final boolean isMacroExpansion = (included.getKind() == CAstEntity.MACRO_ENTITY);

      System.err.println("found " + included.getName() + " for " + CAstPrinter.print(n));

      final CAstEntity copy = (new CAstCloner(new CAstImpl(), true) {

        private CAstNode copyIncludeExpr(CAstNode expr) {
          if (expr.getValue() != null) {
            return Ast.makeConstant(expr.getValue());
          } else if (expr instanceof CAstOperator) {
            return expr;
          } else {
            CAstNode nc[] = new CAstNode[expr.getChildCount()];

            for (int i = 0; i < expr.getChildCount(); i++) {
              nc[i] = copyIncludeExpr(expr.getChild(i));
            }

            return Ast.makeNode(expr.getKind(), nc);
          }
        }

    }
        protected CAstNode copyNodes(CAstNode root, final CAstControlFlowMap cfg, NonCopyingContext c, Map, CAstNode> nodeMap) {
          if (isMacroExpansion && root.getKind() == CAstNode.MACRO_VAR) {
            int arg = ((Number) root.getChild(0).getValue()).intValue();
            CAstNode expr = copyIncludeExpr(n.getChild(arg));
            nodeMap.put(Pair.make(root, c.key()), expr);
            return expr;
          } else {
            return super.copyNodesHackForEclipse(root, cfg, c, nodeMap);
          }
        }
      }).rewrite(included);

      if (copy.getAST() == null) {
        System.err.println((copy.getName() + " has no AST"));

      } else {
        visit(copy.getAST(), new DelegatingContext(wc) {
          public CAstSourcePositionMap getSourceMap() {
            return copy.getSourceMap();
          }

          public CAstControlFlowMap getControlFlow() {
            return copy.getControlFlow();
          }
        }, visitor);

        visitor.visitScopedEntities(copy, copy.getAllScopedEntities(), wc, visitor);
      }
    }
  }

  protected final void walkEntities(CAstEntity N, WalkContext c) {
    visitEntities(N, c, this);
  }

  public final class RootContext implements WalkContext {
    private final Scope globalScope;
    
    private final CAstEntity N;

    private final ModuleEntry module;

    private final Map entityNames = new LinkedHashMap();

   public RootContext(CAstEntity N, ModuleEntry module) {
      this.N = N;
      this.module = module;
      this.globalScope = makeGlobalScope();
    }

    public ModuleEntry getModule() {
      return module;
    }

    public String file() {
      return module.getName();
    }

    public CAstEntity top() {
      return N;

    public Scope currentScope() {
      return globalScope;
    }

    public Set entityScopes() {
      return Collections.singleton(globalScope);
    }

    public CAstSourcePositionMap getSourceMap() {
      return N.getSourceMap();
    }

    public CAstControlFlowMap getControlFlow() {
      return N.getControlFlow();
    }

    public IncipientCFG cfg() {
      return null;
    }

    public UnwindState getUnwindState() {
      return null;
    }

    public String getName() {
      return null;
    }

    public void setCatchType(int blockNumber, TypeReference catchType) {
    }

    public void setCatchType(CAstNode castNode, TypeReference catchType) {
    }

    public TypeReference[][] getCatchTypes() {
      return null;
    }
    
    public void addEntityName(CAstEntity e, String name) {
      entityNames.put(e, name);
    }

    public String getEntityName(CAstEntity e) {
      if (e == null) {
        return null;
      } else {
        assert entityNames.containsKey(e);
        return "L" + entityNames.get(e);

    public boolean hasValue(CAstNode n) {
      assert false;
      return false;
    }

    public int setValue(CAstNode n, int v) {
      assert false;
      return 0;
    }

    public int getValue(CAstNode n) {
      assert false;
      return -1;
    }

    public Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet) {
      assert false;
      return null;
    }

    public Set getAccesses(CAstEntity e) {
      assert false;
      return null;
    }

    public Scope getGlobalScope(){
      return globalScope;
    }
    
  };

  /**
   * translate module, represented by {@link CAstEntity} N
   */
  public void translate(final CAstEntity N, final ModuleEntry module) {
    if (DEBUG_TOP)
      System.err.println(("translating " + module.getName()));
    // this.inlinedSourceMap = inlinedSourceMap;
    final ExposedNamesCollector exposedNamesCollector = new ExposedNamesCollector();
    exposedNamesCollector.run(N);
    entity2ExposedNames = exposedNamesCollector.getEntity2ExposedNames();
    // CAstEntity rewrite = (new ExposedParamRenamer(new CAstImpl(),
    // entity2ExposedNames)).rewrite(N);
    walkEntities(N, new RootContext(N, module));
  }

  public void translate(final CAstEntity N, final WalkContext context) {
    final ExposedNamesCollector exposedNamesCollector = new ExposedNamesCollector();
    exposedNamesCollector.run(N);
    entity2ExposedNames = exposedNamesCollector.getEntity2ExposedNames();
    walkEntities(N, context);
  }
 
}
=======
/******************************************************************************
 * Copyright (c) 2002 - 2006 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.cast.ir.translator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.cast.ir.ssa.AssignInstruction;
import com.ibm.wala.cast.ir.ssa.AstAssertInstruction;
import com.ibm.wala.cast.ir.ssa.AstConstants;
import com.ibm.wala.cast.ir.ssa.AstEchoInstruction;
import com.ibm.wala.cast.ir.ssa.AstGlobalRead;
import com.ibm.wala.cast.ir.ssa.AstGlobalWrite;
import com.ibm.wala.cast.ir.ssa.AstIsDefinedInstruction;
import com.ibm.wala.cast.ir.ssa.AstLexicalAccess;
import com.ibm.wala.cast.ir.ssa.AstLexicalAccess.Access;
import com.ibm.wala.cast.ir.ssa.AstLexicalRead;
import com.ibm.wala.cast.ir.ssa.AstLexicalWrite;
import com.ibm.wala.cast.ir.ssa.EachElementGetInstruction;
import com.ibm.wala.cast.ir.ssa.EachElementHasNextInstruction;
import com.ibm.wala.cast.ir.ssa.SSAConversion;
import com.ibm.wala.cast.loader.AstMethod.DebuggingInformation;
import com.ibm.wala.cast.loader.AstMethod.LexicalInformation;
import com.ibm.wala.cast.loader.CAstAbstractLoader;
import com.ibm.wala.cast.tree.CAstControlFlowMap;
import com.ibm.wala.cast.tree.CAstEntity;
import com.ibm.wala.cast.tree.CAstNode;
import com.ibm.wala.cast.tree.CAstSourcePositionMap;
import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
import com.ibm.wala.cast.tree.CAstSymbol;
import com.ibm.wala.cast.tree.CAstType;
import com.ibm.wala.cast.tree.impl.CAstImpl;
import com.ibm.wala.cast.tree.impl.CAstOperator;
import com.ibm.wala.cast.tree.impl.CAstSymbolImpl;
import com.ibm.wala.cast.tree.impl.CAstSymbolImplBase;
import com.ibm.wala.cast.tree.rewrite.CAstCloner;
import com.ibm.wala.cast.tree.rewrite.CAstRewriter;
import com.ibm.wala.cast.tree.visit.CAstVisitor;
import com.ibm.wala.cast.types.AstTypeReference;
import com.ibm.wala.cast.util.CAstPrinter;
import com.ibm.wala.cfg.AbstractCFG;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.IClassLoader;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.ModuleEntry;
import com.ibm.wala.shrikeBT.BinaryOpInstruction;
import com.ibm.wala.shrikeBT.ConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.ShiftInstruction;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAMonitorInstruction;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.INodeWithNumber;
import com.ibm.wala.util.graph.impl.SparseNumberedGraph;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.warnings.Warning;

/**
 * Common code to translate CAst to IR. Must be specialized by each language to
 * handle semantics appropriately.
 */
public abstract class AstTranslator extends CAstVisitor implements ArrayOpHandler, TranslatorToIR {

  /**
   * set to true to use new handling of lexical scoping
   */
  public static final boolean NEW_LEXICAL = true;


  /**
   * does the language care about using type-appropriate default values? For
   * Java, the answer is yes (ints should get a default value of 0, null for
   * pointers, etc.). For JavaScript, the answer is no, as any variable can hold
   * the value 'undefined'.
   */
  protected abstract boolean useDefaultInitValues();

  /**
   * can lexical reads / writes access globals?
   */
  protected abstract boolean treatGlobalsAsLexicallyScoped();

  /**
   * given accesses in a method to variables defined in an enclosing lexical
   * scope, is it legal to read the variable into a local l once at the
   * beginning of the method, operate on l through the method body (rather than
   * performing separate lexical read / write operations), and write back the
   * value in l (if necessary) at the end of the method?
   */
  protected abstract boolean useLocalValuesForLexicalVars();

  protected boolean topLevelFunctionsInGlobalScope() {
    return true;
  }

  /**
   * for a block that catches all exceptions, what is the root exception type
   * that it can catch? E.g., for Java, java.lang.Throwable
   */
  protected abstract TypeReference defaultCatchType();

  protected abstract TypeReference makeType(CAstType type);

  /**
   * @return
   * define a new (presumably nested) type. return true if type was successfully
   * defined, false otherwise
   */
  protected abstract boolean defineType(CAstEntity type, WalkContext wc);

  /**
   * declare a new function, represented by N
   */
  protected abstract void declareFunction(CAstEntity N, WalkContext context);

  /**
   * fully define a function. invoked after all the code of the function has
   * been processed
   */
  protected abstract void defineFunction(CAstEntity N, WalkContext definingContext, AbstractCFG cfg, SymbolTable symtab,
      boolean hasCatchBlock, TypeReference[][] caughtTypes, boolean hasMonitorOp, AstLexicalInformation lexicalInfo,
      DebuggingInformation debugInfo);

  /**
   * define a new field fieldEntity within topEntity
   */
  protected abstract void defineField(CAstEntity topEntity, WalkContext context, CAstEntity fieldEntity);

  /**
   * create the language-appropriate name for f
   */
  protected abstract String composeEntityName(WalkContext parent, CAstEntity f);

  /**
   * generate IR for a CAst throw expression, updating context.cfg()
   */
  protected abstract void doThrow(WalkContext context, int exception);

  /**
   * generate IR for a CAst array read, updating context.cfg()
   */
  public abstract void doArrayRead(WalkContext context, int result, int arrayValue, CAstNode arrayRef, int[] dimValues);

  /**
   * generate IR for a CAst array write, updating context.cfg()
   */
  public abstract void doArrayWrite(WalkContext context, int arrayValue, CAstNode arrayRef, int[] dimValues, int rval);

  /**
   * generate IR for a CAst field read, updating context.cfg()
   */
  protected abstract void doFieldRead(WalkContext context, int result, int receiver, CAstNode elt, CAstNode parent);

  /**
   * generate IR for a CAst field write, updating context.cfg()
   */
  protected abstract void doFieldWrite(WalkContext context, int receiver, CAstNode elt, CAstNode parent, int rval);

  /**
   * generate IR for a CAst function expression, updating context.cfg()
   */
  protected abstract void doMaterializeFunction(CAstNode node, WalkContext context, int result, int exception, CAstEntity fn);

  /**
   * generate IR for a CAst new expression, updating context.cfg()
   */
  protected abstract void doNewObject(WalkContext context, CAstNode newNode, int result, Object type, int[] arguments);

  /**
   * generate IR for a CAst method call expression, updating context.cfg()
   */
  protected abstract void doCall(WalkContext context, CAstNode call, int result, int exception, CAstNode name, int receiver,
      int[] arguments);

  /**
   * used to generate instructions for array operations; defaults to this
   */
  private ArrayOpHandler arrayOpHandler;

  protected boolean isExceptionLabel(Object label) {
    if (label == null)
      return false;
    if (label instanceof Boolean)
      return false;
    if (label instanceof Number)
      return false;
    if (label == CAstControlFlowMap.SWITCH_DEFAULT)
      return false;
    return true;
  }

  /**
   * If this returns true, new global declarations get created for any attempt
   * to access a non-existent variable (believe it or not, JavaScript actually
   * does this!)
   */
  protected boolean hasImplicitGlobals() {
    return false;
  }

  /**
   * If this returns true, then attempts to lookup non-existent names return
   * `null' rather than tripping an assertion. This can be used when special
   * handling is needed for built-in names. (PHP does this)
   */
  protected boolean hasSpecialUndeclaredVariables() {
    return false;
  }

  /**
   * some languages let you omit initialization of certain fields when writing
   * an object literal (e.g., PHP). This method should be overridden to handle
   * such cases.
   */
  protected void handleUnspecifiedLiteralKey(WalkContext context, CAstNode objectLiteralNode, int unspecifiedLiteralIndex,
      CAstVisitor visitor) {
    Assertions.UNREACHABLE();
  }

  /**
   * generate prologue code for each function body
   */
  protected void doPrologue(WalkContext context) {
    // if we are SSA converting lexical accesses, add a placeholder instruction
    // eventually (via mutation of its Access array) reads all relevant lexical
    // variables at the beginning of the method.
    if (useLocalValuesForLexicalVars()) {
      context.cfg().addInstruction(new AstLexicalRead(new Access[0]));
    } else {
      // perform a lexical write to copy the value stored in the local
      // associated with each parameter to the lexical name
      final CAstEntity entity = context.top();
      Set exposedNames = entity2ExposedNames.get(entity);
      if (exposedNames != null) {
        for (String arg : entity.getArgumentNames()) {
          if (exposedNames.contains(arg)) {
            final Scope currentScope = context.currentScope();
            Symbol symbol = currentScope.lookup(arg);
            assert symbol.getDefiningScope() == currentScope;
            int argVN = symbol.valueNumber();
            Access A = new Access(arg, context.getEntityName(entity), argVN);
            context.cfg().addInstruction(new AstLexicalWrite(A));
          }
        }
      }
    }
  }

  /**
   * generate IR for call modeling creation of primitive value, updating
   * context.cfg()
   */
  protected abstract void doPrimitive(int resultVal, WalkContext context, CAstNode primitiveCall);

  /**
   * get the value number for a name defined locally (i.e., within the current
   * method) by looking up the name in context.currentScope(). Note that the
   * caller is responsible for ensuring that name is defined in the local scope.
   */
  protected int doLocalRead(WalkContext context, String name) {
    if (!useLocalValuesForLexicalVars()) {
      CAstEntity entity = context.top();
      Set exposed = entity2ExposedNames.get(entity);
      if (exposed != null && exposed.contains(name)) {
        return doLexReadHelper(context, name);
      }
    }
    return context.currentScope().lookup(name).valueNumber();
  }

  /**
   * add an {@link AssignInstruction} to context.cfg() that copies rval to the
   * value number of local nm. Note that the caller is responsible for ensuring
   * that nm is defined in the local scope.
   */
  protected void doLocalWrite(WalkContext context, String nm, int rval) {
    if (!useLocalValuesForLexicalVars()) {
      CAstEntity entity = context.top();
      Set exposed = entity2ExposedNames.get(entity);
      if (exposed != null && exposed.contains(nm)) {
        // use a lexical write
        doLexicallyScopedWrite(context, nm, rval);
        return;
      }
    }
    int lval = context.currentScope().lookup(nm).valueNumber();
    if (lval != rval) {
      context.cfg().addInstruction(new AssignInstruction(lval, rval));
    }
  }

  /**
   * Note that the caller is responsible for ensuring that name is defined in a
   * lexical scope.
   * 
   * @param node
   *          the AST node representing the read
   * @param context
   * @param name
   */
  protected int doLexicallyScopedRead(CAstNode node, WalkContext context, final String name) {
    return doLexReadHelper(context, name);
  }

  /**
   * we only have this method to avoid having to pass a node parameter at other
   * call sites, as would be required for
   * {@link #doLexicallyScopedRead(CAstNode, WalkContext, String)}
   */
  private int doLexReadHelper(WalkContext context, final String name) {
    Symbol S = context.currentScope().lookup(name);
    Scope definingScope = S.getDefiningScope();
    CAstEntity E = definingScope.getEntity();
    // record in declaring scope that the name is exposed to a nested scope
//<<<<<<< .mine
//    Symbol S = context.currentScope().lookup(name);
//    CAstEntity E = S.getDefiningScope().getEntity();
//    addExposedName(E, E, name, S.getDefiningScope().lookup(name).valueNumber(), false, context);
//=======
    addExposedName(E, E, name, definingScope.lookup(name).valueNumber(), false, context);
//>>>>>>> .r4421

    final String entityName = context.getEntityName(E);
    if (useLocalValuesForLexicalVars()) {
      // lexically-scoped variables can be given a single vn in a method
//<<<<<<< .mine
//      Access A = new Access(name, context.getEntityName(E), vn);
//=======
//>>>>>>> .r4421

//<<<<<<< .mine
      // (context.top() is current entity)
      // record the name as exposed for the current entity, since if the name is
      // updated via a call to a nested function, SSA for the current entity may
      // need to be updated with the new definition
//      addExposedName(context.top(), E, name, vn, false, context);
//=======
      markExposedInEnclosingEntities(context, name, definingScope, E, entityName, false);
//>>>>>>> .r4421

//<<<<<<< .mine
      // record the access; later, the Accesses in the instruction
      // defining vn will be adjusted based on this information; see
      // patchLexicalAccesses()
//      addAccess(context, context.top(), A);
//=======
      return S.valueNumber();
//>>>>>>> .r4421

    } else {
      // lexically-scoped variables should be read from their scope each time
      int result = context.currentScope().allocateTempValue();
//<<<<<<< .mine
//      Access A = new Access(name, context.getEntityName(E), result);
//=======
      Access A = new Access(name, entityName, result);
//>>>>>>> .r4421
      context.cfg().addInstruction(new AstLexicalRead(A));
      markExposedInEnclosingEntities(context, name, definingScope, E, entityName, false);
      return result;
    }
  }

  /**
   * record name as exposed for the current entity and for all enclosing
   * entities up to that of the defining scope, since if the name is updated via
   * a call to a nested function, SSA for these entities may need to be updated
   * with the new definition
   * 
   * @param context
   * @param name
   * @param definingScope
   * @param E
   * @param entityName
   * @param isWrite
   */
  private void markExposedInEnclosingEntities(WalkContext context, final String name, Scope definingScope, CAstEntity E,
      final String entityName, boolean isWrite) {
    Scope curScope = context.currentScope();
    while (!curScope.equals(definingScope)) {
      final Symbol curSymbol = curScope.lookup(name);
      final int vn = curSymbol.valueNumber();
      final Access A = new Access(name, entityName, vn);
      final CAstEntity entity = curScope.getEntity();
      if (entity != definingScope.getEntity()) {
        addExposedName(entity, E, name, vn, isWrite, context);
        // record the access; later, the Accesses in the instruction
        // defining vn will be adjusted based on this information; see
        // patchLexicalAccesses()
        addAccess(context, entity, A);
      }
      curScope = curScope.getParent();
    }
  }

  /**
   * Note that the caller is responsible for ensuring that name is defined in a
   * lexical scope.
   * 
   */
  protected void doLexicallyScopedWrite(WalkContext context, String name, int rval) {
    Symbol S = context.currentScope().lookup(name);
    Scope definingScope = S.getDefiningScope();
    CAstEntity E = definingScope.getEntity();
    // record in declaring scope that the name is exposed to a nested scope
    addExposedName(E, E, name, definingScope.lookup(name).valueNumber(), true, context);

    if (useLocalValuesForLexicalVars()) {
      // lexically-scoped variables can be given a single vn in a method
      
      markExposedInEnclosingEntities(context, name, definingScope, E, context.getEntityName(E), true);

      context.cfg().addInstruction(new AssignInstruction(S.valueNumber(), rval));
      // we add write instructions at every access for now
      // eventually, we may restructure the method to do a single combined write
      // before exit
      Access A = new Access(name, context.getEntityName(E), rval);
      context.cfg().addInstruction(new AstLexicalWrite(A));

    } else {
      // lexically-scoped variables must be written in their scope each time
      Access A = new Access(name, context.getEntityName(E), rval);
      context.cfg().addInstruction(new AstLexicalWrite(A));
      markExposedInEnclosingEntities(context, name, definingScope, E, context.getEntityName(E), true);
    }
  }

  /**
   * generate instructions for a read of a global
   */
  protected int doGlobalRead(CAstNode node, WalkContext context, String name) {
    Symbol S = context.currentScope().lookup(name);

    // Global variables can be treated as lexicals defined in the CG root, or
    if (treatGlobalsAsLexicallyScoped()) {

      // lexically-scoped variables can be given a single vn in a method, or
      if (useLocalValuesForLexicalVars()) {
        int vn = S.valueNumber();
        Access A = new Access(name, null, vn);

        addExposedName(context.top(), null, name, vn, false, context);
        addAccess(context, context.top(), A);

        return vn;

        // lexically-scoped variables can be read from their scope each time
      } else {
        int result = context.currentScope().allocateTempValue();
        Access A = new Access(name, null, result);
        context.cfg().addInstruction(new AstLexicalRead(A));
        addAccess(context, context.top(), A);
        return result;
      }

      // globals can be treated as a single static location
    } else {
      int result = context.currentScope().allocateTempValue();
      FieldReference global = makeGlobalRef(name);
      context.cfg().addInstruction(new AstGlobalRead(result, global));
      return result;
    }
  }

  /**
   * generate instructions for a write of a global
   */
  protected void doGlobalWrite(WalkContext context, String name, int rval) {
    Symbol S = context.currentScope().lookup(name);

    // Global variables can be treated as lexicals defined in the CG root, or
    if (treatGlobalsAsLexicallyScoped()) {

      // lexically-scoped variables can be given a single vn in a method, or
      if (useLocalValuesForLexicalVars()) {
        int vn = S.valueNumber();
        Access A = new Access(name, null, vn);

        addExposedName(context.top(), null, name, vn, true, context);
        addAccess(context, context.top(), A);

        context.cfg().addInstruction(new AssignInstruction(vn, rval));
        context.cfg().addInstruction(new AstLexicalWrite(A));

        // lexically-scoped variables can be read from their scope each time
      } else {
        Access A = new Access(name, null, rval);
        context.cfg().addInstruction(new AstLexicalWrite(A));
        addAccess(context, context.top(), A);
      }

      // globals can be treated as a single static location
    } else {
      FieldReference global = makeGlobalRef(name);
      context.cfg().addInstruction(new AstGlobalWrite(global, rval));
    }
  }

  /**
   * generate instructions to check if ref has field, storing answer in result
   */
  protected void doIsFieldDefined(WalkContext context, int result, int ref, CAstNode field) {
    Assertions.UNREACHABLE();
  }

  /**
   * creates a reference to a global named globalName. the declaring type and
   * type of the global are both the root type.
   */
  protected FieldReference makeGlobalRef(String globalName) {
    TypeReference rootTypeRef = TypeReference.findOrCreate(loader.getReference(), AstTypeReference.rootTypeName);
    return FieldReference.findOrCreate(rootTypeRef, Atom.findOrCreateUnicodeAtom("global " + globalName), rootTypeRef);
  }

  protected final IClassLoader loader;

  /**
   * for handling languages that let you include other source files named
   * statically (e.g., ABAP)
   */
  protected final Map namedEntityResolver;

  protected final SSAInstructionFactory insts;

  protected AstTranslator(IClassLoader loader, Map namedEntityResolver, ArrayOpHandler arrayOpHandler) {
    this.loader = loader;
    this.namedEntityResolver = namedEntityResolver;
    this.arrayOpHandler = arrayOpHandler!=null? arrayOpHandler: this;
    this.insts = loader.getInstructionFactory();
  }

  protected AstTranslator(IClassLoader loader, Map namedEntityResolver) {
    this(loader, namedEntityResolver, null);
  }
  
  protected AstTranslator(IClassLoader loader) {
    this(loader, null);
  }

  /**
   * for keeping position information for the generated SSAInstructions and SSA
   * locals
   */
      return kind == EXIT;
  private static class AstDebuggingInformation implements DebuggingInformation {
    private Position codeBodyPosition;

    private String[][] valueNumberNames;

    private Position[] instructionPositions;

    AstDebuggingInformation(Position codeBodyPosition, Position[] instructionPositions, String[] names) {
      this.codeBodyPosition = codeBodyPosition;

      this.instructionPositions = instructionPositions;

      valueNumberNames = new String[names.length][];
      for (int i = 0; i < names.length; i++) {
        if (names[i] != null) {
          valueNumberNames[i] = new String[] { names[i] };
        } else {
          valueNumberNames[i] = new String[0];
        }
      }
    }

    public Position getCodeBodyPosition() {
      return codeBodyPosition;
    }

    public Position getInstructionPosition(int instructionOffset) {
      return instructionPositions[instructionOffset];
    }

    public String[][] getSourceNamesForValues() {
      return valueNumberNames;
    }
  }

  public static final boolean DEBUG_ALL = false;

  public static final boolean DEBUG_TOP = DEBUG_ALL || false;

  public static final boolean DEBUG_CFG = DEBUG_ALL || false;

  public static final boolean DEBUG_NAMES = DEBUG_ALL || false;

  public static final boolean DEBUG_LEXICAL = DEBUG_ALL || false;

  /**
   * basic block implementation used in the CFGs constructed during the
   * IR-generating AST traversal
   */
  protected final static class PreBasicBlock implements INodeWithNumber, IBasicBlock {
    private static final int NORMAL = 0;

    private static final int HANDLER = 1;

    private static final int ENTRY = 2;

    private static final int EXIT = 3;

    private int kind = NORMAL;

    private int number = -1;

    private int firstIndex = -1;

    private int lastIndex = -2;

    private final List instructions = new ArrayList();

    public int getNumber() {
      return getGraphNodeId();
    }

    public int getGraphNodeId() {
      return number;
    }

    public void setGraphNodeId(int number) {
      this.number = number;
    }

    public int getFirstInstructionIndex() {
      return firstIndex;
    }

    void setFirstIndex(int firstIndex) {
      this.firstIndex = firstIndex;
    }

    public int getLastInstructionIndex() {
      return lastIndex;
    }

    void setLastIndex(int lastIndex) {
      this.lastIndex = lastIndex;
    }

    void makeExitBlock() {
      kind = EXIT;
    }

    void makeEntryBlock() {
      kind = ENTRY;
    }

    void makeHandlerBlock() {
      kind = HANDLER;
    }

    public boolean isEntryBlock() {
      return kind == ENTRY;
    }

    public boolean isExitBlock() {
    }

    public boolean isHandlerBlock() {
      return kind == HANDLER;
    }

    public String toString() {
      return "PreBB" + number + ":" + firstIndex + ".." + lastIndex;
    }

    List instructions() {
      return instructions;
    }

    public boolean isCatchBlock() {
      return (lastIndex > -1) && (instructions.get(0) instanceof SSAGetCaughtExceptionInstruction);
    }

    public IMethod getMethod() {
      return null;
    }

    public Iterator iterator() {
      return instructions.iterator();
    }
  }

  protected final class UnwindState {
    final CAstNode unwindAst;

    final WalkContext astContext;

    final CAstVisitor astVisitor;

    UnwindState(CAstNode unwindAst, WalkContext astContext, CAstVisitor astVisitor) {
      this.unwindAst = unwindAst;
      this.astContext = astContext;
      this.astVisitor = astVisitor;
    }

    public UnwindState getParent() {
      return astContext.getUnwindState();
    }

    public int hashCode() {
      return astContext.hashCode() * unwindAst.hashCode() * astVisitor.hashCode();
    }

    public boolean equals(Object o) {
      if (o instanceof UnwindState) {
        if (((UnwindState) o).unwindAst != unwindAst)
          return false;
        if (((UnwindState) o).astVisitor != astVisitor)
          return false;
        if (getParent() == null) {
          return ((UnwindState) o).getParent() == null;
        } else {
          return getParent().equals(((UnwindState) o).getParent());
        }
      }

      return false;
    }

    boolean covers(UnwindState other) {
      if (equals(other))
        return true;
      if (getParent() != null)
        return getParent().covers(other);
      return false;
    }
  }

  /**
   * holds the control-flow graph as it is being constructed. When construction
   * is complete, information is stored in an {@link AstCFG}
   */
  public final class IncipientCFG extends SparseNumberedGraph {

    protected class Unwind {
      private final Map unwindData = new LinkedHashMap();

      /**
       * a cache of generated blocks
       */
      private final Map>, PreBasicBlock> code = new LinkedHashMap>, PreBasicBlock>();

      void setUnwindState(PreBasicBlock block, UnwindState context) {
        unwindData.put(block, context);
      }

      void setUnwindState(CAstNode node, UnwindState context) {
        unwindData.put(nodeToBlock.get(node), context);
      }

      /**
       * When adding an edge from source to target, it is possible that certain
       * exception-handling code needs to be executed before the control is
       * actually transfered to target. This method determines if this is the
       * case, and if so, it generates the exception handler blocks and adds an
       * appropriate edge to the target. It returns the basic block that should
       * be the target of the edge from source (target itself if there is no
       * exception-handling code, the initial catch block otherwise)
       */
      public PreBasicBlock findOrCreateCode(PreBasicBlock source, PreBasicBlock target, final boolean exception) {
        UnwindState sourceContext = unwindData.get(source);
        final CAstNode dummy = exception ? (new CAstImpl()).makeNode(CAstNode.EMPTY) : null;

        // no unwinding is needed, so jump to target block directly
        if (sourceContext == null)
          return target;

        WalkContext astContext = sourceContext.astContext;
        UnwindState targetContext = null;
        if (target != null)
          targetContext = unwindData.get(target);

        // in unwind context, but catch in same (or inner) unwind context
        if (targetContext != null && targetContext.covers(sourceContext))
          return target;

        Pair> key = Pair.make(sourceContext, Pair.make(target, exception));

        if (code.containsKey(key)) {
          return code.get(key);

        } else {
          int e = -1;
          PreBasicBlock currentBlock = getCurrentBlock();
          if (!isDeadBlock(currentBlock)) {
            addInstruction(insts.GotoInstruction());
            newBlock(false);
          }
          PreBasicBlock startBlock = getCurrentBlock();
          if (exception) {
            setCurrentBlockAsHandler();
            e = sourceContext.astContext.currentScope().allocateTempValue();
            addInstruction(insts.GetCaughtExceptionInstruction(startBlock.getNumber(), e));
            sourceContext.astContext.setCatchType(startBlock.getNumber(), defaultCatchType());
          }

          while (sourceContext != null && (targetContext == null || !targetContext.covers(sourceContext))) {
            final CAstRewriter.Rewrite ast = (new CAstCloner(new CAstImpl()) {
              protected CAstNode flowOutTo(Map, CAstNode> nodeMap, CAstNode oldSource, Object label,
                  CAstNode oldTarget, CAstControlFlowMap orig, CAstSourcePositionMap src) {
                if (exception && !isExceptionLabel(label)) {
                  return dummy;
                } else {
                  return oldTarget;
                }
              }
            }).copy(sourceContext.unwindAst, sourceContext.astContext.getControlFlow(), sourceContext.astContext.getSourceMap(),
                sourceContext.astContext.top().getNodeTypeMap(), sourceContext.astContext.top().getAllScopedEntities());
            sourceContext.astVisitor.visit(ast.newRoot(), new DelegatingContext(sourceContext.astContext) {
              public CAstSourcePositionMap getSourceMap() {
                return ast.newPos();
              }

              public CAstControlFlowMap getControlFlow() {
                return ast.newCfg();
              }
            }, sourceContext.astVisitor);

            sourceContext = sourceContext.getParent();
          }

          PreBasicBlock endBlock = getCurrentBlock();
          if (exception) {
            addPreNode(dummy);
            doThrow(astContext, e);
          } else {
            addInstruction(insts.GotoInstruction());
          }
          newBlock(false);

          addEdge(currentBlock, getCurrentBlock());
          if (target != null) {
            addEdge(endBlock, target);

            // `null' target is idiom for branch/throw to exit
          } else {
            addDelayedEdge(endBlock, exitMarker, exception);
          }

          code.put(key, startBlock);
          return startBlock;
        }
      }
    }

    private Unwind unwind = null;

    private final List blocks = new ArrayList();

    private final Map nodeToBlock = new LinkedHashMap();

    private final Map>> delayedEdges = new LinkedHashMap>>();

    private final Object exitMarker = new Object();

    private final Set deadBlocks = new LinkedHashSet();

    private final Set normalToExit = new LinkedHashSet();

    private final Set exceptionalToExit = new LinkedHashSet();

    private Position[] linePositions = new Position[10];

    private boolean hasCatchBlock = false;

    /**
     * does the method have any monitor operations?
     */
    private boolean hasMonitorOp = false;

    private int currentInstruction = 0;

    private PreBasicBlock currentBlock;

    public int getCurrentInstruction() {
      return currentInstruction;
    }

    public PreBasicBlock getCurrentBlock() {
      return currentBlock;
    }

    boolean hasCatchBlock() {
      return hasCatchBlock;
    }

    boolean hasMonitorOp() {
      return hasMonitorOp;
    }

    void noteCatchBlock() {
      hasCatchBlock = true;
    }

    Position[] getLinePositionMap() {
      return linePositions;
    }

    /**
     * create a new basic block, and set it as the current block.
     * 
     * @param fallThruFromPrior
     *          should a fall-through edge be added from the previous block
     *          (value of currentBlock at entry)? if false, the newly created
     *          block is marked as a dead block, as it has no incoming edges.
     * @return the new block
     */
    public PreBasicBlock newBlock(boolean fallThruFromPrior) {
      // optimization: if we have a fall-through from an empty block, just
      // return the empty block
      if (fallThruFromPrior && !currentBlock.isEntryBlock() && currentBlock.instructions().size() == 0) {
        return currentBlock;
      }

      PreBasicBlock previous = currentBlock;
      currentBlock = new PreBasicBlock();
      addNode(currentBlock);
      blocks.add(currentBlock);

      if (DEBUG_CFG)
        System.err.println(("adding new block (node) " + currentBlock));
      if (fallThruFromPrior) {
        if (DEBUG_CFG)
          System.err.println(("adding fall-thru edge " + previous + " --> " + currentBlock));
        addEdge(previous, currentBlock);
      } else {
        deadBlocks.add(currentBlock);
      }

      return currentBlock;
    }

    /**
     * record a delayed edge addition from src to dst. Edge will be added when
     * appropriate; see {@link #checkForRealizedEdges(CAstNode)} and
     * {@link #checkForRealizedExitEdges(PreBasicBlock)}
     */
    private void addDelayedEdge(PreBasicBlock src, Object dst, boolean exception) {
      MapUtil.findOrCreateSet(delayedEdges, dst).add(Pair.make(src, exception));
    }

    void makeEntryBlock(PreBasicBlock bb) {
      bb.makeEntryBlock();
    }

    void makeExitBlock(PreBasicBlock bb) {
      bb.makeExitBlock();

      for (Iterator ps = getPredNodes(bb); ps.hasNext();)
        normalToExit.add(ps.next());

      // now that we have created the exit block, add the delayed edges to the
      // exit
      checkForRealizedExitEdges(bb);
    }

    void setCurrentBlockAsHandler() {
      currentBlock.makeHandlerBlock();
    }

    boolean hasDelayedEdges(CAstNode n) {
      return delayedEdges.containsKey(n);
    }

    /**
     * given some n which is now mapped by nodeToBlock, add any delayed edges to
     * n's block
     */
    private void checkForRealizedEdges(CAstNode n) {
      if (delayedEdges.containsKey(n)) {
        for (Iterator> ss = delayedEdges.get(n).iterator(); ss.hasNext();) {
          Pair s = ss.next();
          PreBasicBlock src = s.fst;
          boolean exception = s.snd;
          if (unwind == null) {
            addEdge(src, nodeToBlock.get(n));
          } else {
            PreBasicBlock target = nodeToBlock.get(n);
            addEdge(src, unwind.findOrCreateCode(src, target, exception));
          }
        }

        delayedEdges.remove(n);
      }
    }

    /**
     * add any delayed edges to the exit block
     */
    private void checkForRealizedExitEdges(PreBasicBlock exitBlock) {
      if (delayedEdges.containsKey(exitMarker)) {
        for (Iterator> ss = delayedEdges.get(exitMarker).iterator(); ss.hasNext();) {
          Pair s = ss.next();
          PreBasicBlock src = s.fst;
          boolean exception = s.snd;
          addEdge(src, exitBlock);
          if (exception)
            exceptionalToExit.add(src);
          else
            normalToExit.add(src);
        }

        delayedEdges.remove(exitMarker);
      }
    }

    private void setUnwindState(CAstNode node, UnwindState context) {
      if (unwind == null)
        unwind = new Unwind();
      unwind.setUnwindState(node, context);
    }

    public void addPreNode(CAstNode n) {
      addPreNode(n, null);
    }

    /**
     * associate n with the current block, and update the current unwind state
     */
    public void addPreNode(CAstNode n, UnwindState context) {
      if (DEBUG_CFG)
        System.err.println(("adding pre-node " + n));
      nodeToBlock.put(n, currentBlock);
      deadBlocks.remove(currentBlock);
      if (context != null)
        setUnwindState(n, context);
      // now that we've associated n with a block, add associated delayed edges
      checkForRealizedEdges(n);
    }

    public void addPreEdge(CAstNode src, CAstNode dst, boolean exception) {
      assert nodeToBlock.containsKey(src);
      addPreEdge(nodeToBlock.get(src), dst, exception);
    }

    /**
     * if dst is associated with a basic block b, add an edge from src to b.
     * otherwise, record the edge addition as delayed.
     */
    public void addPreEdge(PreBasicBlock src, CAstNode dst, boolean exception) {
      if (dst == CAstControlFlowMap.EXCEPTION_TO_EXIT) {
        assert exception;
        addPreEdgeToExit(src, exception);
      } else if (nodeToBlock.containsKey(dst)) {
        PreBasicBlock target = nodeToBlock.get(dst);
        if (DEBUG_CFG)
          System.err.println(("adding pre-edge " + src + " --> " + dst));
        if (unwind == null) {
          addEdge(src, target);
        } else {
          addEdge(src, unwind.findOrCreateCode(src, target, exception));
        }
      } else {
        if (DEBUG_CFG)
          System.err.println(("adding delayed pre-edge " + src + " --> " + dst));
        addDelayedEdge(src, dst, exception);
      }
    }

    public void addPreEdgeToExit(CAstNode src, boolean exception) {
      assert nodeToBlock.containsKey(src);
      addPreEdgeToExit(nodeToBlock.get(src), exception);
    }

    public void addPreEdgeToExit(PreBasicBlock src, boolean exception) {
      if (unwind != null) {
        PreBasicBlock handlers = unwind.findOrCreateCode(src, null, exception);
        if (handlers != null) {
          addEdge(src, handlers);
          return;
        }
      }

      addDelayedEdge(src, exitMarker, exception);
    }

    public void addEdge(PreBasicBlock src, PreBasicBlock dst) {
      super.addEdge(src, dst);
      deadBlocks.remove(dst);
    }

    boolean isDeadBlock(PreBasicBlock block) {
      return deadBlocks.contains(block);
    }

    public PreBasicBlock getBlock(CAstNode n) {
      return nodeToBlock.get(n);
    }

    /**
     * mark the current position as the position for the instruction
     */
    private void noteLinePosition(int instruction) {
      if (linePositions.length < (instruction + 1)) {
        Position[] newData = new Position[instruction * 2 + 1];
        System.arraycopy(linePositions, 0, newData, 0, linePositions.length);
        linePositions = newData;
      }

      linePositions[instruction] = getCurrentPosition();
    }

    public void addInstruction(SSAInstruction n) {
      deadBlocks.remove(currentBlock);

      int inst = currentInstruction++;

      noteLinePosition(inst);

      if (currentBlock.instructions().size() == 0) {
        currentBlock.setFirstIndex(inst);
      } else {
        assert !(n instanceof SSAGetCaughtExceptionInstruction);
      }

      if (DEBUG_CFG) {
        System.err.println(("adding " + n + " at " + inst + " to " + currentBlock));
      }

      if (n instanceof SSAMonitorInstruction) {
        hasMonitorOp = true;
      }

      currentBlock.instructions().add(n);

      currentBlock.setLastIndex(inst);
    }
  }

  /**
   * data structure for the final CFG for a method, based on the information in
   * an {@link IncipientCFG}
   */
  protected final static class AstCFG extends AbstractCFG {
    private final SSAInstruction[] instructions;

    private final int[] instructionToBlockMap;

    private final String functionName;

    private final SymbolTable symtab;

    AstCFG(CAstEntity n, IncipientCFG icfg, SymbolTable symtab) {
      super(null);
      List blocks = icfg.blocks;

      this.symtab = symtab;
      functionName = n.getName();
      instructionToBlockMap = new int[blocks.size()];

      for (int i = 0; i < blocks.size(); i++)
        instructionToBlockMap[i] = blocks.get(i).getLastInstructionIndex();

      for (int i = 0; i < blocks.size(); i++) {
        PreBasicBlock block = blocks.get(i);
        this.addNode(block);
        if (block.isCatchBlock()) {
          setCatchBlock(i);
        }

        if (DEBUG_CFG)
    public boolean isCaseInsensitive() {
          System.err.println(("added " + blocks.get(i) + " to final CFG as " + getNumber(blocks.get(i))));
      }
      if (DEBUG_CFG)
        System.err.println((getMaxNumber() + " blocks total"));

      init();

      for (int i = 0; i < blocks.size(); i++) {
        PreBasicBlock src = blocks.get(i);
        for (Iterator j = icfg.getSuccNodes(src); j.hasNext();) {
          PreBasicBlock dst = (PreBasicBlock) j.next();
          if (isCatchBlock(dst.getNumber()) || (dst.isExitBlock() && icfg.exceptionalToExit.contains(src))) {
            if (DEBUG_CFG)
              System.err.println(("exceptonal edge " + src + " -> " + dst));
            addExceptionalEdge(src, dst);
          }

          if (dst.isExitBlock() ? icfg.normalToExit.contains(src) : !isCatchBlock(dst.getNumber())) {
            if (DEBUG_CFG)
              System.err.println(("normal edge " + src + " -> " + dst));
            addNormalEdge(src, dst);
          }
        }
      }

      int x = 0;
      instructions = new SSAInstruction[icfg.currentInstruction];
      for (int i = 0; i < blocks.size(); i++) {
        List bi = blocks.get(i).instructions();
        for (int j = 0; j < bi.size(); j++) {
          instructions[x++] = bi.get(j);
        }
      }
    }

    public int hashCode() {
      return functionName.hashCode();
    }

    public boolean equals(Object o) {
      return (o instanceof AstCFG) && functionName.equals(((AstCFG) o).functionName);
    }

    public PreBasicBlock getBlockForInstruction(int index) {
      for (int i = 1; i < getNumberOfNodes() - 1; i++)
        if (index <= instructionToBlockMap[i])
          return getNode(i);

      return null;
    }

    public SSAInstruction[] getInstructions() {
      return instructions;
    }

    public int getProgramCounter(int index) {
      return index;
    }

    public String toString() {
      SSAInstruction[] insts = (SSAInstruction[]) getInstructions();
      StringBuffer s = new StringBuffer("CAst CFG of " + functionName);
      int params[] = symtab.getParameterValueNumbers();
      for (int i = 0; i < params.length; i++)
        s.append(" ").append(params[i]);
      s.append("\n");

      for (int i = 0; i < getNumberOfNodes(); i++) {
        PreBasicBlock bb = (PreBasicBlock) getNode(i);
        s.append(bb).append("\n");

        for (Iterator ss = getSuccNodes(bb); ss.hasNext();)
          s.append("    -->" + ss.next() + "\n");

        for (int j = bb.getFirstInstructionIndex(); j <= bb.getLastInstructionIndex(); j++)
          if (insts[j] != null)
            s.append("  " + insts[j].toString(symtab) + "\n");
      }

      s.append("-- END --");
      return s.toString();
    }
  }

  public static enum ScopeType {
    LOCAL, GLOBAL, SCRIPT, FUNCTION, TYPE
  };

  private static final boolean DEBUG = false;

  protected class FinalCAstSymbol implements CAstSymbol {
    private final String _name;

    private FinalCAstSymbol(String _name) {
      this._name = _name;
    }

    public String name() {
      return _name;
    }

    public boolean isFinal() {
      return true;
    }

      return false;
    }

    public boolean isInternalName() {
      return false;
    }

    public Object defaultInitValue() {
      return null;
    }
  }

  public static class InternalCAstSymbol extends CAstSymbolImplBase {
    public InternalCAstSymbol(String _name) {
      super(_name, false, false, null);
    }

    public InternalCAstSymbol(String _name, boolean _isFinal) {
      super(_name, _isFinal, false, null);
    }

    public InternalCAstSymbol(String _name, boolean _isFinal, boolean _isCaseInsensitive) {
      super(_name, _isFinal, _isCaseInsensitive, null);
    }

    public InternalCAstSymbol(String _name, boolean _isFinal, boolean _isCaseInsensitive, Object _defaultInitValue) {
      super(_name, _isFinal, _isCaseInsensitive, _defaultInitValue);
    }

    public boolean isInternalName() {
      return true;
    }
  }

  /**
   * interface for name information stored in a symbol table.
   * 
   * @see Scope
   */
  protected interface Symbol {
    int valueNumber();

    Scope getDefiningScope();

    boolean isParameter();

    Object constant();

    void setConstant(Object s);

    boolean isFinal();

    boolean isInternalName();

    Object defaultInitValue();
  }

  /**
   * a scope in the symbol table built during AST traversal
   */
  public interface Scope {

    ScopeType type();

    int allocateTempValue();

    int getConstantValue(Object c);

    boolean isConstant(int valueNumber);

    Object getConstantObject(int valueNumber);

    void declare(CAstSymbol s);

    void declare(CAstSymbol s, int valueNumber);

    boolean isCaseInsensitive(String name);

    boolean contains(String name);

    Symbol lookup(String name);

    Iterator getAllNames();

    int size();

    boolean isGlobal(Symbol s);

    boolean isLexicallyScoped(Symbol s);

    CAstEntity getEntity();

    Scope getParent();
  }

  private static abstract class AbstractSymbol implements Symbol {
    private Object constantValue;

    private boolean isFinalValue;

    private final Scope definingScope;

    private Object defaultValue;

    AbstractSymbol(Scope definingScope, boolean isFinalValue, Object defaultValue) {
      this.definingScope = definingScope;
      this.isFinalValue = isFinalValue;
      this.defaultValue = defaultValue;
    }

    public boolean isFinal() {
      return isFinalValue;
    }

    public Object defaultInitValue() {
      return defaultValue;
    }

    public Object constant() {
      return constantValue;
    }

    public void setConstant(Object cv) {
      constantValue = cv;
    }

    public Scope getDefiningScope() {
      return definingScope;
    }
  };

  private abstract class AbstractScope implements Scope {
    private final Scope parent;

    private final Map values = new LinkedHashMap();

    private final Map caseInsensitiveNames = new LinkedHashMap();

    protected abstract SymbolTable getUnderlyingSymtab();

    public Scope getParent() {
      return parent;
    }

    public int size() {
      return getUnderlyingSymtab().getMaxValueNumber() + 1;
    }

    public Iterator getAllNames() {
      return values.keySet().iterator();
    }

    public int allocateTempValue() {
      return getUnderlyingSymtab().newSymbol();
    }

    public int getConstantValue(Object o) {
      if (o instanceof Integer) {
        return getUnderlyingSymtab().getConstant(((Integer) o).intValue());
      } else if (o instanceof Float) {
        return getUnderlyingSymtab().getConstant(((Float) o).floatValue());
      } else if (o instanceof Double) {
            }
        return getUnderlyingSymtab().getConstant(((Double) o).doubleValue());
      } else if (o instanceof Long) {
        return getUnderlyingSymtab().getConstant(((Long) o).longValue());
      } else if (o instanceof String) {
        return getUnderlyingSymtab().getConstant((String) o);
      } else if (o instanceof Boolean) {
        return getUnderlyingSymtab().getConstant((Boolean) o);
      } else if (o instanceof Character) {
        return getUnderlyingSymtab().getConstant(((Character) o).charValue());
      } else if (o instanceof Byte) {
        return getUnderlyingSymtab().getConstant(((Byte) o).byteValue());
      } else if (o instanceof Short) {
        return getUnderlyingSymtab().getConstant(((Short) o).shortValue());
      } else if (o == null) {
        return getUnderlyingSymtab().getNullConstant();
      } else if (o == CAstControlFlowMap.SWITCH_DEFAULT) {
        return getUnderlyingSymtab().getConstant("__default label");
      } else {
        System.err.println(("cannot handle constant " + o));
        Assertions.UNREACHABLE();
        return -1;
      }
    }

    public boolean isConstant(int valueNumber) {
      return getUnderlyingSymtab().isConstant(valueNumber);
    }

    public Object getConstantObject(int valueNumber) {
      return getUnderlyingSymtab().getConstantValue(valueNumber);
    }

    public void declare(CAstSymbol s, int vn) {
      String nm = s.name();
      assert !contains(nm) : nm;
      if (s.isCaseInsensitive())
        caseInsensitiveNames.put(nm.toLowerCase(), nm);
      values.put(nm, makeSymbol(s, vn));
    }

    public void declare(CAstSymbol s) {
      String nm = s.name();
      if (!contains(nm) || lookup(nm).getDefiningScope() != this) {
        if (s.isCaseInsensitive())
          caseInsensitiveNames.put(nm.toLowerCase(), nm);
        values.put(nm, makeSymbol(s));
      } else {
        assert !s.isFinal() : "trying to redeclare " + nm;
      }
    }

    AbstractScope(Scope parent) {
      this.parent = parent;
    }

    private final String mapName(String nm) {
          }
      String mappedName = caseInsensitiveNames.get(nm.toLowerCase());
      return (mappedName == null) ? nm : mappedName;
    }

    protected Symbol makeSymbol(CAstSymbol s) {
      return makeSymbol(s.name(), s.isFinal(), s.isInternalName(), s.defaultInitValue(), -1, this);
    }

    protected Symbol makeSymbol(CAstSymbol s, int vn) {
      return makeSymbol(s.name(), s.isFinal(), s.isInternalName(), s.defaultInitValue(), vn, this);
    }

    abstract protected Symbol makeSymbol(String nm, boolean isFinal, boolean isInternalName, Object defaultInitValue, int vn,
        Scope parent);

    public boolean isCaseInsensitive(String nm) {
      return caseInsensitiveNames.containsKey(nm.toLowerCase());
    }

    public Symbol lookup(String nm) {
      if (contains(nm)) {
        return values.get(mapName(nm));
      } else {
        Symbol scoped = parent.lookup(nm);
        if (scoped != null && getEntityScope() == this && (isGlobal(scoped) || isLexicallyScoped(scoped))) {
          values.put(nm,
              makeSymbol(nm, scoped.isFinal(), scoped.isInternalName(), scoped.defaultInitValue(), -1, scoped.getDefiningScope()));
          if (scoped.getDefiningScope().isCaseInsensitive(nm)) {
            caseInsensitiveNames.put(nm.toLowerCase(), nm);
          }
          return values.get(nm);
        } else {
          return scoped;
        }
      }
    }

    public boolean contains(String nm) {
      String mappedName = caseInsensitiveNames.get(nm.toLowerCase());

      return values.containsKey(mappedName == null ? nm : mappedName);
    }

    public boolean isGlobal(Symbol s) {
      return s.getDefiningScope().type() == ScopeType.GLOBAL;
    }

    public abstract boolean isLexicallyScoped(Symbol s);

    protected abstract AbstractScope getEntityScope();

    public abstract CAstEntity getEntity();
  };

  private AbstractScope makeScriptScope(final CAstEntity s, Scope parent) {
    return new AbstractScope(parent) {
      SymbolTable scriptGlobalSymtab = new SymbolTable(s.getArgumentCount());

      public SymbolTable getUnderlyingSymtab() {
        return scriptGlobalSymtab;
      }

      protected AbstractScope getEntityScope() {
        return this;
      }

      public boolean isLexicallyScoped(Symbol s) {
        if (isGlobal(s))
          return false;
        else
          return ((AbstractScope) s.getDefiningScope()).getEntity() != getEntity();
      }

      public CAstEntity getEntity() {
        return s;
      }

      public ScopeType type() {
        return ScopeType.SCRIPT;
      }

      protected Symbol makeSymbol(final String nm, final boolean isFinal, final boolean isInternalName,
          final Object defaultInitValue, int vn, Scope definer) {
        final int v = vn == -1 ? getUnderlyingSymtab().newSymbol() : vn;
        if (useDefaultInitValues() && defaultInitValue != null) {
          if (getUnderlyingSymtab().getValue(v) == null) {
            setDefaultValue(getUnderlyingSymtab(), v, defaultInitValue);
          }
        }
        return new AbstractSymbol(definer, isFinal, defaultInitValue) {
          public String toString() {
            return nm + ":" + System.identityHashCode(this);
          }

          public int valueNumber() {
            return v;
          }

          public boolean isInternalName() {
            return isInternalName;

          public boolean isParameter() {
            return false;
          }
        };
      }
    };
  }

  protected int getArgumentCount(CAstEntity f) {
    return f.getArgumentCount();
  }
  
  protected String[] getArgumentNames(CAstEntity f) {
    return f.getArgumentNames();
  }
  
  private AbstractScope makeFunctionScope(final CAstEntity f, Scope parent) {
    return new AbstractScope(parent) {
      private final String[] params = getArgumentNames(f);

      private final SymbolTable functionSymtab = new SymbolTable(getArgumentCount(f));

      // ctor for scope object
      {
        for (int i = 0; i < getArgumentCount(f); i++) {
          final int yuck = i;
          declare(new CAstSymbol() {
            public String name() {
              return params[yuck];
            }

            public boolean isFinal() {
              return false;
            }

            public boolean isCaseInsensitive() {
              return false;
            }

            public boolean isInternalName() {
              return false;
            }

            public Object defaultInitValue() {
              return null;
          });
        }
      }

      public SymbolTable getUnderlyingSymtab() {
        return functionSymtab;
      }

      protected AbstractScope getEntityScope() {
        return this;
      }

      public boolean isLexicallyScoped(Symbol s) {
        if (isGlobal(s))
          return false;
        else
          return ((AbstractScope) s.getDefiningScope()).getEntity() != getEntity();
      }

      public CAstEntity getEntity() {
        return f;
      }

      public ScopeType type() {
        return ScopeType.FUNCTION;
      }

      private int find(String n) {
        for (int i = 0; i < params.length; i++) {
          if (n.equals(params[i])) {
            return i + 1;
          }
        }

        return -1;
      }

      protected Symbol makeSymbol(final String nm, final boolean isFinal, final boolean isInternalName,
          final Object defaultInitValue, final int valueNumber, Scope definer) {
        return new AbstractSymbol(definer, isFinal, defaultInitValue) {
          final int vn;

          {
            int x = find(nm);
            if (x != -1) {
              assert valueNumber == -1;
              vn = x;
            } else if (valueNumber != -1) {
              vn = valueNumber;
            } else {
              vn = getUnderlyingSymtab().newSymbol();
            }
            if (useDefaultInitValues() && defaultInitValue != null) {
              if (getUnderlyingSymtab().getValue(vn) == null) {
                setDefaultValue(getUnderlyingSymtab(), vn, defaultInitValue);
              }
            }
          }

          public String toString() {
            return nm + ":" + System.identityHashCode(this);
          }

          public int valueNumber() {
            return vn;
          }

          public boolean isInternalName() {
            return isInternalName;
          }

          public boolean isParameter() {
            return vn <= params.length;
          }
        };
      }
    };
  }

  private Scope makeLocalScope(CAstNode s, final Scope parent) {
    return new AbstractScope(parent) {
      public ScopeType type() {
        return ScopeType.LOCAL;
      }

      public SymbolTable getUnderlyingSymtab() {
        return ((AbstractScope) parent).getUnderlyingSymtab();
      }

      protected AbstractScope getEntityScope() {
        return ((AbstractScope) parent).getEntityScope();
      }

      public boolean isLexicallyScoped(Symbol s) {
        return ((AbstractScope) getEntityScope()).isLexicallyScoped(s);
      }

      public CAstEntity getEntity() {
        return ((AbstractScope) getEntityScope()).getEntity();
      }

      protected Symbol makeSymbol(final String nm, boolean isFinal, final boolean isInternalName, final Object defaultInitValue,
          int vn, Scope definer) {
        final int v = vn == -1 ? getUnderlyingSymtab().newSymbol() : vn;
        if (useDefaultInitValues() && defaultInitValue != null) {
          if (getUnderlyingSymtab().getValue(v) == null) {
          } else if (hasSpecialUndeclaredVariables()) {
            setDefaultValue(getUnderlyingSymtab(), v, defaultInitValue);
          }
        }
        return new AbstractSymbol(definer, isFinal, defaultInitValue) {
          public String toString() {
            return nm + ":" + System.identityHashCode(this);
          }

          public int valueNumber() {
            return v;
          }

          public boolean isInternalName() {
            return isInternalName;
          }

          public boolean isParameter() {
            return false;
          }
        };
      }
    };
  }

  private Scope makeGlobalScope() {
    final Map globalSymbols = new LinkedHashMap();
    final Map caseInsensitiveNames = new LinkedHashMap();
    return new Scope() {
      private final String mapName(String nm) {
        String mappedName = caseInsensitiveNames.get(nm.toLowerCase());
        return (mappedName == null) ? nm : mappedName;
      }

      public Scope getParent() {
        return null;
      }

      public boolean isGlobal(Symbol s) {
        return true;
      }

      public boolean isLexicallyScoped(Symbol s) {
        return false;
      }

      public CAstEntity getEntity() {
        return null;
      }

      public int size() {
        return globalSymbols.size();
      }

      public Iterator getAllNames() {
        return globalSymbols.keySet().iterator();
      }

      public int allocateTempValue() {
        throw new UnsupportedOperationException();
      }

      public int getConstantValue(Object c) {
        throw new UnsupportedOperationException();
      }

      public boolean isConstant(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public Object getConstantObject(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public ScopeType type() {
        return ScopeType.GLOBAL;
      }

      public boolean contains(String name) {
        return hasImplicitGlobals() || globalSymbols.containsKey(mapName(name));
      }

      public boolean isCaseInsensitive(String name) {
        return caseInsensitiveNames.containsKey(name.toLowerCase());
      }

      public Symbol lookup(final String name) {
        if (!globalSymbols.containsKey(mapName(name))) {
          if (hasImplicitGlobals()) {
            declare(new CAstSymbol() {
              public String name() {
                return name;
              }

              public boolean isFinal() {
                return false;
              }

              public boolean isCaseInsensitive() {
                return false;
              }

              public boolean isInternalName() {
                return false;
              }

              public Object defaultInitValue() {
                return null;
              }
            });
    }
            return null;
          } else {
            throw new Error("cannot find " + name);
          }
        }

        return globalSymbols.get(mapName(name));
      }

      public void declare(CAstSymbol s, int vn) {
        assert vn == -1;
        declare(s);
      }

      public void declare(final CAstSymbol s) {
        final String name = s.name();
        if (s.isCaseInsensitive()) {
          caseInsensitiveNames.put(name.toLowerCase(), name);
        }
        globalSymbols.put(name, new AbstractSymbol(this, s.isFinal(), s.defaultInitValue()) {
          public String toString() {
            return name + ":" + System.identityHashCode(this);
          }

          public boolean isParameter() {
            return false;
          }

          public boolean isInternalName() {
            return s.isInternalName();
          }

          public int valueNumber() {
            throw new UnsupportedOperationException();
          }
        });
      }
    };
  }

  protected Scope makeTypeScope(final CAstEntity type, final Scope parent) {
    final Map typeSymbols = new LinkedHashMap();
    final Map caseInsensitiveNames = new LinkedHashMap();
    return new Scope() {
      private final String mapName(String nm) {
        String mappedName = caseInsensitiveNames.get(nm.toLowerCase());
        return (mappedName == null) ? nm : mappedName;
      }

      public Scope getParent() {
        return parent;
      }

      public boolean isGlobal(Symbol s) {
        return false;
      }

      public boolean isLexicallyScoped(Symbol s) {
        return false;
      }

      public CAstEntity getEntity() {
        return type;
      }

      public int size() {
        return typeSymbols.size();
      }

      public Iterator getAllNames() {
        return typeSymbols.keySet().iterator();
      }

      public int allocateTempValue() {
        throw new UnsupportedOperationException();
      }

      public int getConstantValue(Object c) {
        throw new UnsupportedOperationException();
      }

      public boolean isConstant(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public Object getConstantObject(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public ScopeType type() {
        return ScopeType.TYPE;
      }

      public boolean contains(String name) {
        return typeSymbols.containsKey(mapName(name));
      }

      public boolean isCaseInsensitive(String name) {
        return caseInsensitiveNames.containsKey(name.toLowerCase());
      }

      public Symbol lookup(String nm) {
        if (typeSymbols.containsKey(mapName(nm)))
          return typeSymbols.get(mapName(nm));
        else {
          return parent.lookup(nm);
        }
      }


      public void declare(CAstSymbol s, int vn) {
        assert vn == -1;
        declare(s);
      }

      public void declare(final CAstSymbol s) {
        final String name = s.name();
        assert !s.isFinal();
        if (s.isCaseInsensitive())
          caseInsensitiveNames.put(name.toLowerCase(), name);
        typeSymbols.put(name, new AbstractSymbol(this, s.isFinal(), s.defaultInitValue()) {
          public String toString() {
            return name + ":" + System.identityHashCode(this);
          }

          public boolean isParameter() {
            return false;
          }

          public boolean isInternalName() {
            return s.isInternalName();
          }

          public int valueNumber() {
            throw new UnsupportedOperationException();
          }
        });
      }
    };
  }

  public interface WalkContext extends CAstVisitor.Context {

    ModuleEntry getModule();

    String getName();

    String file();

    CAstSourcePositionMap getSourceMap();

    CAstControlFlowMap getControlFlow();

    Scope currentScope();

    Set entityScopes();

    IncipientCFG cfg();

    UnwindState getUnwindState();

    void setCatchType(int blockNumber, TypeReference catchType);

    void setCatchType(CAstNode catchNode, TypeReference catchType);

    TypeReference[][] getCatchTypes();

    void addEntityName(CAstEntity e, String name);
    
    String getEntityName(CAstEntity e);

    boolean hasValue(CAstNode n);

    int setValue(CAstNode n, int v);

    int getValue(CAstNode n);
    
    Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet);
    
    Set getAccesses(CAstEntity e);
    
    Scope getGlobalScope();
    
  }

  private abstract class DelegatingContext implements WalkContext {
    private final WalkContext parent;

    DelegatingContext(WalkContext parent) {
      this.parent = parent;
    }

    public Set getAccesses(CAstEntity e) {
      return parent.getAccesses(e);
    }

    public ModuleEntry getModule() {
      return parent.getModule();
    }

    public String getName() {
      return parent.getName();
    }

    public String file() {
      return parent.file();
    }

    public CAstEntity top() {
      return parent.top();
    }

    public CAstSourcePositionMap getSourceMap() {
      return parent.getSourceMap();
    }

    public CAstControlFlowMap getControlFlow() {
      return parent.getControlFlow();
    }

    public Scope currentScope() {
      return parent.currentScope();
    }

    public Set entityScopes() {
      return parent.entityScopes();
    }

    public IncipientCFG cfg() {
      return parent.cfg();
    }

    public UnwindState getUnwindState() {
      return parent.getUnwindState();
    }

    public void setCatchType(int blockNumber, TypeReference catchType) {
      parent.setCatchType(blockNumber, catchType);
    }

    public void setCatchType(CAstNode catchNode, TypeReference catchType) {
      parent.setCatchType(catchNode, catchType);
    }

    public TypeReference[][] getCatchTypes() {
      return parent.getCatchTypes();
    }

    public void addEntityName(CAstEntity e, String name) {
      parent.addEntityName(e, name);
    }
    
    public String getEntityName(CAstEntity e) {
      return parent.getEntityName(e);
    }
    
    public boolean hasValue(CAstNode n) {
      return parent.hasValue(n);
    }

    public int setValue(CAstNode n, int v) {
      return parent.setValue(n, v);
    }

    public int getValue(CAstNode n) {
      return parent.getValue(n);
    }

    public Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet) {
      return parent.exposeNameSet(entity, writeSet);
    }

    public Scope getGlobalScope() {
      return parent.getGlobalScope();
    }
    
  }

  private class FileContext extends DelegatingContext {
    private final String fUnitName;

    public FileContext(WalkContext parent, String unitName) {
      super(parent);
      fUnitName = unitName;
    }

    public String getName() {
      return fUnitName;
    }
  }

  private class UnwindContext extends DelegatingContext {
    private final UnwindState state;

    UnwindContext(CAstNode unwindNode, WalkContext parent, CAstVisitor visitor) {
      super(parent);
      this.state = new UnwindState(unwindNode, parent, visitor);
    }

    public UnwindState getUnwindState() {
      return state;
    }
  }

  private abstract class EntityContext extends DelegatingContext {
    protected final CAstEntity topNode;

    protected final String name;

    EntityContext(WalkContext parent, CAstEntity s) {
      super(parent);
      this.topNode = s;
      this.name = composeEntityName(parent, s);
      addEntityName(s, this.name);
    }

    public String getName() {
      return name;
    }

    public CAstEntity top() {
      return topNode;
    }

    public CAstSourcePositionMap getSourceMap() {
      return top().getSourceMap();
    }

  }

  private class CodeEntityContext extends EntityContext {
    private final Scope topEntityScope;

    private final Set allEntityScopes;

    private final IncipientCFG cfg;

    private TypeReference[][] catchTypes = new TypeReference[0][];

    Set, Integer>> exposedReads;
    Set, Integer>> exposedWrites;
    
    Set accesses;
    
    /**
     * maps nodes in the current function to the value number holding their value
     * or, for constants, to their constant value.
     */
    private final Map results = new LinkedHashMap();

    CodeEntityContext(WalkContext parent, Scope entityScope, CAstEntity s) {
      super(parent, s);

      this.topEntityScope = entityScope;

      this.allEntityScopes = HashSetFactory.make();
      this.allEntityScopes.add(entityScope);

      cfg = new IncipientCFG();
    }

    public Set getAccesses(CAstEntity e) {
      if (e == topNode) {
        if (accesses == null) {
          accesses = HashSetFactory.make();
        }
        return accesses;
      } else {
        return super.getAccesses(e);
      }
    }
    
    @Override
    public Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet) {
      if (entity == topNode) {
       if (writeSet) {
         if (exposedWrites == null) {
           exposedWrites = HashSetFactory.make();
         }
         return exposedWrites;
       } else {
         if (exposedReads == null) {
           exposedReads = HashSetFactory.make();
         }
         return exposedReads;         
       }
      } else {
        return super.exposeNameSet(entity, writeSet);
      }
    }


    public CAstControlFlowMap getControlFlow() {
      return top().getControlFlow();
    }

    public IncipientCFG cfg() {
      return cfg;
    }

    public Scope currentScope() {
      return topEntityScope;
    }

    public Set entityScopes() {
      return allEntityScopes;
    }

    public UnwindState getUnwindState() {
      return null;
    }

    public void setCatchType(CAstNode catchNode, TypeReference catchType) {
      setCatchType(cfg.getBlock(catchNode).getNumber(), catchType);
    }

    public void setCatchType(int blockNumber, TypeReference catchType) {
      if (catchTypes.length <= blockNumber) {
        TypeReference[][] data = new TypeReference[blockNumber + 1][];
        System.arraycopy(catchTypes, 0, data, 0, catchTypes.length);
        catchTypes = data;
      }

      if (catchTypes[blockNumber] == null) {
        catchTypes[blockNumber] = new TypeReference[] { catchType };
      } else {
        TypeReference[] data = catchTypes[blockNumber];

        for (int i = 0; i < data.length; i++) {
          if (data[i] == catchType) {
            return;
          }
        }

        TypeReference[] newData = new TypeReference[data.length + 1];
        System.arraycopy(data, 0, newData, 0, data.length);
        newData[data.length] = catchType;

        catchTypes[blockNumber] = newData;
      }
    }

    public TypeReference[][] getCatchTypes() {
      return catchTypes;
    }
    
    public boolean hasValue(CAstNode n) {
      return results.containsKey(n);
    }

    public final int setValue(CAstNode n, int v) {
      results.put(n, new Integer(v));
      return v;
    }

    public final int getValue(CAstNode n) {
      if (results.containsKey(n))
        return results.get(n).intValue();
      else {
        if (DEBUG) {
          System.err.println(("no value for " + n.getKind()));
        }
        return -1;
      }
    }

  }

  private final class TypeContext extends EntityContext {

    private TypeContext(WalkContext parent, CAstEntity n) {
      super(parent, n);
    }

    public CAstControlFlowMap getControlFlow() {
      Assertions.UNREACHABLE("TypeContext.getControlFlow()");
      return null;
    }

    public IncipientCFG cfg() {
      Assertions.UNREACHABLE("TypeContext.cfg()");
      return null;
    }

    public UnwindState getUnwindState() {
      Assertions.UNREACHABLE("TypeContext.getUnwindState()");
      return null;
    }
  }

  private class LocalContext extends DelegatingContext {
    private final Scope localScope;

    LocalContext(WalkContext parent, Scope localScope) {
      super(parent);
      this.localScope = localScope;
      parent.entityScopes().add(localScope);
    }

    public Scope currentScope() {
      return localScope;
    }
  }

  /**
   * lexical access information for some entity scope. used during call graph
   * construction to handle lexical accesses.
   */
  public static class AstLexicalInformation implements LexicalInformation {
    /**
     * the name of this function, as it appears in the definer portion of a
     * lexical name
     */
    private final String functionLexicalName;

    /**
     * names possibly accessed in a nested lexical scope, represented as pairs
     * (name,nameOfDefiningEntity)
     */
    private final Pair[] exposedNames;

    /**
     * map from instruction index and exposed name (via its index in
     * {@link #exposedNames}) to the value number for the name at that
     * instruction index. This can vary at different instructions due to SSA
     * (and this information is updated during {@link SSAConversion}).
     */
    private final int[][] instructionLexicalUses;

    /**
     * maps each exposed name (via its index in {@link #exposedNames}) to its
     * value number at method exit.
     */
    private final int[] exitLexicalUses;

    /**
     * the names of the enclosing methods declaring names that are lexically
     * accessed by the entity
     */
    private final String[] scopingParents;

    /**
     * all value numbers appearing as entries in {@link #instructionLexicalUses}
     * and {@link #exitLexicalUses}, computed lazily
     */
    private MutableIntSet allExposedUses = null;

    /**
     * names of exposed variables of this method that cannot be written outside
     */
    private final Set readOnlyNames;

    @SuppressWarnings("unchecked")
    public AstLexicalInformation(AstLexicalInformation original) {
  }
      this.functionLexicalName = original.functionLexicalName;

      if (original.exposedNames != null) {
        exposedNames = new Pair[original.exposedNames.length];
        for (int i = 0; i < exposedNames.length; i++) {
          exposedNames[i] = Pair.make(original.exposedNames[i].fst, original.exposedNames[i].snd);
        }
      } else {
        exposedNames = null;
      }

      instructionLexicalUses = new int[original.instructionLexicalUses.length][];
      for (int i = 0; i < instructionLexicalUses.length; i++) {
        int[] x = original.instructionLexicalUses[i];
        if (x != null) {
          instructionLexicalUses[i] = new int[x.length];
          for (int j = 0; j < x.length; j++) {
            instructionLexicalUses[i][j] = x[j];
          }
        }
      }

      if (original.exitLexicalUses != null) {
        exitLexicalUses = new int[original.exitLexicalUses.length];
        for (int i = 0; i < exitLexicalUses.length; i++) {
          exitLexicalUses[i] = original.exitLexicalUses[i];
        }
      } else {
        exitLexicalUses = null;
      }

      if (original.scopingParents != null) {
        scopingParents = new String[original.scopingParents.length];
        for (int i = 0; i < scopingParents.length; i++) {
          scopingParents[i] = original.scopingParents[i];
        }
      } else {
        scopingParents = null;
      }

      readOnlyNames = original.readOnlyNames;
    }

    private int[] buildLexicalUseArray(Pair, Integer>[] exposedNames, String entityName) {
      if (exposedNames != null) {
        int[] lexicalUses = new int[exposedNames.length];
        for (int j = 0; j < exposedNames.length; j++) {
          if (entityName == null || entityName.equals(exposedNames[j].fst.snd)) {
            lexicalUses[j] = exposedNames[j].snd;
          } else {
            lexicalUses[j] = -1;
          }
        }

        return lexicalUses;
      } else {
        return null;
      }
    }

    private Pair[] buildLexicalNamesArray(Pair, Integer>[] exposedNames) {
      if (exposedNames != null) {
        @SuppressWarnings("unchecked")
        Pair[] lexicalNames = new Pair[exposedNames.length];
        for (int j = 0; j < exposedNames.length; j++) {
          lexicalNames[j] = exposedNames[j].fst;
        }

        return lexicalNames;
      } else {
        return null;
      }
    }

    @SuppressWarnings("unchecked")
    AstLexicalInformation(String entityName, Scope scope, SSAInstruction[] instrs,
        Set, Integer>> exposedNamesForReadSet,
        Set, Integer>> exposedNamesForWriteSet, Set accesses) {
      this.functionLexicalName = entityName;

      Pair, Integer>[] EN = null;
      if (exposedNamesForReadSet != null || exposedNamesForWriteSet != null) {
        Set, Integer>> exposedNamesSet = new HashSet, Integer>>();
        if (exposedNamesForReadSet != null) {
          exposedNamesSet.addAll(exposedNamesForReadSet);
        }
        if (exposedNamesForWriteSet != null) {
          exposedNamesSet.addAll(exposedNamesForWriteSet);
        }
        EN = exposedNamesSet.toArray(new Pair[exposedNamesSet.size()]);
      }

      if (exposedNamesForReadSet != null) {
        Set readOnlyNames = new HashSet();
        for (Pair, Integer> v : exposedNamesForReadSet) {
      return exposedNames;
          if (entityName != null && entityName.equals(v.fst.snd)) {
            readOnlyNames.add(v.fst.fst);
          }
        }
        if (exposedNamesForWriteSet != null) {
          for (Pair, Integer> v : exposedNamesForWriteSet) {
            if (entityName != null && entityName.equals(v.fst.snd)) {
              readOnlyNames.remove(v.fst.fst);
            }
          }
        }
        this.readOnlyNames = readOnlyNames;
      } else {
        this.readOnlyNames = null;
      }

      this.exposedNames = buildLexicalNamesArray(EN);

      // the value numbers stored in exitLexicalUses and instructionLexicalUses
      // are identical at first; they will be updated
      // as needed during the final SSA conversion
      this.exitLexicalUses = buildLexicalUseArray(EN, entityName);

      this.instructionLexicalUses = new int[instrs.length][];
      for (int i = 0; i < instrs.length; i++) {
        if (instrs[i] instanceof SSAAbstractInvokeInstruction) {
          this.instructionLexicalUses[i] = buildLexicalUseArray(EN, null);
        }
      }

      if (accesses != null) {
        Set parents = new LinkedHashSet();
        for (Iterator ACS = accesses.iterator(); ACS.hasNext();) {
          Access AC = ACS.next();
          if (AC.variableDefiner != null) {
            parents.add(AC.variableDefiner);
          }
        }
        scopingParents = parents.toArray(new String[parents.size()]);

        if (DEBUG_LEXICAL) {
          System.err.println(("scoping parents of " + scope.getEntity()));
          System.err.println(parents.toString());
        }

      } else {
        scopingParents = null;
      }

      if (DEBUG_NAMES) {
        System.err.println(("lexical uses of " + scope.getEntity()));
        for (int i = 0; i < instructionLexicalUses.length; i++) {
          if (instructionLexicalUses[i] != null) {
            System.err.println(("  lexical uses of " + instrs[i]));
            for (int j = 0; j < instructionLexicalUses[i].length; j++) {
  }

              System.err.println(("    " + this.exposedNames[j].fst + ": " + instructionLexicalUses[i][j]));
            }
          }
        }
      }
    }

    public int[] getExitExposedUses() {
      return exitLexicalUses;
    }

    private static final int[] NONE = new int[0];

    public int[] getExposedUses(int instructionOffset) {
      return instructionLexicalUses[instructionOffset] == null ? NONE : instructionLexicalUses[instructionOffset];
    }

    public IntSet getAllExposedUses() {
      if (allExposedUses == null) {
        allExposedUses = IntSetUtil.make();
        if (exitLexicalUses != null) {
          for (int i = 0; i < exitLexicalUses.length; i++) {
            if (exitLexicalUses[i] > 0) {
              allExposedUses.add(exitLexicalUses[i]);
            }
          }
        }
        if (instructionLexicalUses != null) {
          for (int i = 0; i < instructionLexicalUses.length; i++) {
            if (instructionLexicalUses[i] != null) {
              for (int j = 0; j < instructionLexicalUses[i].length; j++) {
                if (instructionLexicalUses[i][j] > 0) {
                  allExposedUses.add(instructionLexicalUses[i][j]);
                }
              }
            }
          }
        }
      }

      return allExposedUses;
    }

    public Pair[] getExposedNames() {
    public String[] getScopingParents() {
      return scopingParents;
    }

    /**
     * reset cached info about value numbers that may have changed
     */
    public void handleAlteration() {
      allExposedUses = null;
    }

    public boolean isReadOnly(String name) {
      return readOnlyNames != null && readOnlyNames.contains(name);
    }

    public String getScopingName() {
      return functionLexicalName;
    }
  };
 
  /**
   * record that in entity e, the access is performed.
   * 
   * If {@link #useLocalValuesForLexicalVars()} is true, the access is performed
   * using a local variable. in
   * {@link #patchLexicalAccesses(SSAInstruction[], Set)}, this information is
   * used to update an instruction that performs all the accesses at the
   * beginning of the method and defines the locals.
   */
  private void addAccess(WalkContext context, CAstEntity e, Access access) {
    context.getAccesses(e).add(access);
  }

  /**
   * Record that a name assigned a value number in the scope of entity may be
   * accessed by a lexically nested scope, i.e., the name may be
   * exposed to lexically nested scopes. This information is needed
   * during call graph construction to properly model the data flow due to the
   * access in the nested scope.
   * 
   * @param entity
   *          an entity in whose scope name is assigned a value number
   * @param declaration
   *          the declaring entity for name (possibly an enclosing scope of
   *          entity, in the case where entity
   *          {@link #useLocalValuesForLexicalVars() accesses the name via a
   *          local})
   * @param name
   *          the accessed name
   * @param valueNumber
   *          the name's value number in the scope of entity
   */
  private void addExposedName(CAstEntity entity, CAstEntity declaration, String name, int valueNumber, boolean isWrite, WalkContext context) {
    Pair, Integer> newVal = Pair.make(Pair.make(name, context.getEntityName(declaration)), valueNumber);
    context.exposeNameSet(entity, isWrite).add(newVal);
  private void setDefaultValue(SymbolTable symtab, int vn, Object value) {
    if (value == CAstSymbol.NULL_DEFAULT_VALUE) {
      symtab.setDefaultValue(vn, null);
    } else {
      symtab.setDefaultValue(vn, value);
    }
  }

  protected IUnaryOpInstruction.IOperator translateUnaryOpcode(CAstNode op) {
    if (op == CAstOperator.OP_BITNOT)
      return AstConstants.UnaryOp.BITNOT;
    else if (op == CAstOperator.OP_NOT)
      return IUnaryOpInstruction.Operator.NEG;
    else if (op == CAstOperator.OP_SUB)
      return AstConstants.UnaryOp.MINUS;
    else if (op == CAstOperator.OP_ADD)
      return AstConstants.UnaryOp.PLUS;
    else
      Assertions.UNREACHABLE("cannot translate " + CAstPrinter.print(op));
    return null;

  }

  protected IBinaryOpInstruction.IOperator translateBinaryOpcode(CAstNode op) {
    if (op == CAstOperator.OP_ADD)
      return BinaryOpInstruction.Operator.ADD;
    else if (op == CAstOperator.OP_DIV)
      return BinaryOpInstruction.Operator.DIV;
    else if (op == CAstOperator.OP_LSH)
      return ShiftInstruction.Operator.SHL;
    else if (op == CAstOperator.OP_MOD)
      return BinaryOpInstruction.Operator.REM;
    else if (op == CAstOperator.OP_MUL)
      return BinaryOpInstruction.Operator.MUL;
    else if (op == CAstOperator.OP_RSH)
      return ShiftInstruction.Operator.SHR;
    else if (op == CAstOperator.OP_SUB)
      return BinaryOpInstruction.Operator.SUB;
    else if (op == CAstOperator.OP_URSH)

      return ShiftInstruction.Operator.USHR;
    else if (op == CAstOperator.OP_BIT_AND)
      return BinaryOpInstruction.Operator.AND;
    else if (op == CAstOperator.OP_BIT_OR)
      return BinaryOpInstruction.Operator.OR;
    else if (op == CAstOperator.OP_BIT_XOR)
      return BinaryOpInstruction.Operator.XOR;
    else if (op == CAstOperator.OP_CONCAT)
      return AstConstants.BinaryOp.CONCAT;
    else if (op == CAstOperator.OP_EQ)
      return AstConstants.BinaryOp.EQ;
    else if (op == CAstOperator.OP_STRICT_EQ)
      return AstConstants.BinaryOp.STRICT_EQ;
    else if (op == CAstOperator.OP_GE)
      return AstConstants.BinaryOp.GE;
    else if (op == CAstOperator.OP_GT)
      return AstConstants.BinaryOp.GT;
    else if (op == CAstOperator.OP_LE)
      return AstConstants.BinaryOp.LE;
    else if (op == CAstOperator.OP_LT)
      return AstConstants.BinaryOp.LT;
    else if (op == CAstOperator.OP_NE)
      return AstConstants.BinaryOp.NE;
    else if (op == CAstOperator.OP_STRICT_NE)
      return AstConstants.BinaryOp.STRICT_NE;
    else {
      Assertions.UNREACHABLE("cannot translate " + CAstPrinter.print(op));
      return null;
    }
  }

  protected IConditionalBranchInstruction.IOperator translateConditionOpcode(CAstNode op) {
    if (op == CAstOperator.OP_EQ)
      return ConditionalBranchInstruction.Operator.EQ;
    else if (op == CAstOperator.OP_GE)
      return ConditionalBranchInstruction.Operator.GE;
    else if (op == CAstOperator.OP_GT)
      return ConditionalBranchInstruction.Operator.GT;
    else if (op == CAstOperator.OP_LE)
      return ConditionalBranchInstruction.Operator.LE;
    else if (op == CAstOperator.OP_LT)
      return ConditionalBranchInstruction.Operator.LT;
    else if (op == CAstOperator.OP_NE)
      return ConditionalBranchInstruction.Operator.NE;

    else {
      Assertions.UNREACHABLE("cannot translate " + CAstPrinter.print(op));
      return null;
    }
  }

  private String[] makeNameMap(CAstEntity n, Set scopes) {
    // all scopes share the same underlying symtab, which is what
    // size really refers to.
    String[] map = new String[scopes.iterator().next().size() + 1];

    if (DEBUG_NAMES) {
      System.err.println(("names array of size " + map.length));
    }

    for (Iterator S = scopes.iterator(); S.hasNext();) {
      Scope scope = S.next();
      for (Iterator I = scope.getAllNames(); I.hasNext();) {
        String nm = I.next();
        Symbol v = (Symbol) scope.lookup(nm);

        if (v.isInternalName()) {
          continue;
        }

        // constants can flow to multiple variables
        if (scope.isConstant(v.valueNumber()))
          continue;

        assert map[v.valueNumber()] == null || map[v.valueNumber()].equals(nm) : "value number " + v.valueNumber()
            + " mapped to multiple names in " + n.getName() + ": " + nm + " and " + map[v.valueNumber()];

        map[v.valueNumber()] = nm;

        if (DEBUG_NAMES) {
          System.err.println(("mapping name " + nm + " to " + v.valueNumber()));
        }
      }
    }

    return map;
  }

  protected final CAstType getTypeForNode(WalkContext context, CAstNode node) {
    if (context.top().getNodeTypeMap() != null) {
      return context.top().getNodeTypeMap().getNodeType(node);
    } else {
      return null;
    }
  }

  /**
   * find any AstLexicalAccess instructions in instrs with a zero access count,
    return !defineType(n, (WalkContext) context);
   * and change them to perform specified accesses. If accesses is empty, null
   * out the pointers to the AstLexicalAccess instructions in the array.
   * 
   * Presumably, such empty AstLexicalAccess instructions should only exist if
   * {@link #useLocalValuesForLexicalVars()} returns true?
   */
  private void patchLexicalAccesses(SSAInstruction[] instrs, Set accesses) {
    Access[] AC = accesses == null || accesses.isEmpty() ? (Access[]) null : (Access[]) accesses.toArray(new Access[accesses.size()]);
    for (int i = 0; i < instrs.length; i++) {
      if (instrs[i] instanceof AstLexicalAccess && ((AstLexicalAccess) instrs[i]).getAccessCount() == 0) {
        // should just be AstLexicalRead for now; may add support for
        // AstLexicalWrite later
        assert instrs[i] instanceof AstLexicalRead;
        assert useLocalValuesForLexicalVars();
        if (AC != null) {
          ((AstLexicalAccess) instrs[i]).setAccesses(AC);
        } else {
          instrs[i] = null;
        }
      }
    }
  }

  private Position getPosition(CAstSourcePositionMap map, CAstNode n) {
    if (map.getPosition(n) != null) {
      return map.getPosition(n);
    } else {
      for (int i = 0; i < n.getChildCount(); i++) {
        Position p = getPosition(map, n.getChild(i));
        if (p != null) {
          return p;
        }
      }

      return null;
    }
  }

  protected WalkContext makeFileContext(WalkContext c, CAstEntity n) {
    return new FileContext((WalkContext) c, n.getName());
  }

  protected WalkContext makeTypeContext(WalkContext c, CAstEntity n) {
    return new TypeContext((WalkContext) c, n);
  }

  protected WalkContext makeCodeContext(WalkContext c, CAstEntity n) {
    WalkContext context = (WalkContext) c;
    AbstractScope scope;
    if (n.getKind() == CAstEntity.SCRIPT_ENTITY)
      scope = makeScriptScope(n, context.currentScope());
    else
      scope = makeFunctionScope(n, context.currentScope());
    return new CodeEntityContext(context, scope, n);
  }


  protected boolean enterEntity(final CAstEntity n, WalkContext context, CAstVisitor visitor) {
    if (DEBUG_TOP)
      System.err.println(("translating " + n.getName()));
    return false;
  }

  protected boolean visitFileEntity(CAstEntity n, WalkContext context, WalkContext fileContext, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFileEntity(CAstEntity n, WalkContext context, WalkContext fileContext, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitFieldEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFieldEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) {
    // Define a new field in the enclosing type, if the language we're
    // processing allows such.
    CAstEntity topEntity = context.top(); // better be a type
    assert topEntity.getKind() == CAstEntity.TYPE_ENTITY : "Parent of field entity is not a type???";
    defineField(topEntity, (WalkContext) context, n);
  }

  protected boolean visitGlobalEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveGlobalEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) {
    // Define a new field in the enclosing type, if the language we're
    // processing allows such.
    context.getGlobalScope().declare(new CAstSymbolImpl(n.getName()));
  }

  protected boolean visitTypeEntity(CAstEntity n, WalkContext context, WalkContext typeContext, CAstVisitor visitor) {
  protected void leaveTypeEntity(CAstEntity n, WalkContext context, WalkContext typeContext, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitFunctionEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    if (n.getAST() == null) // presumably abstract
      declareFunction(n, (WalkContext) context);
    else
      initFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
    return false;
  }

  protected void leaveFunctionEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    if (n.getAST() != null) // non-abstract
      closeFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
  }

  protected boolean visitMacroEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    return true;
  }

  protected boolean visitScriptEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    declareFunction(n, (WalkContext) codeContext);
    initFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
    return false;
  }

  protected void leaveScriptEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    closeFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
  }

  public void initFunctionEntity(final CAstEntity n, WalkContext parentContext, WalkContext functionContext) {
    // entry block
    functionContext.cfg().makeEntryBlock(functionContext.cfg().newBlock(false));
    // first real block
    functionContext.cfg().newBlock(true);
    // prologue code, if any
    doPrologue(functionContext);
  }

  public void closeFunctionEntity(final CAstEntity n, WalkContext parentContext, WalkContext functionContext) {
    // exit block
    functionContext.cfg().makeExitBlock(functionContext.cfg().newBlock(true));

    // create code entry stuff for this entity
    SymbolTable symtab = ((AbstractScope) functionContext.currentScope()).getUnderlyingSymtab();
    TypeReference[][] catchTypes = functionContext.getCatchTypes();
    AstCFG cfg = new AstCFG(n, functionContext.cfg(), symtab);
    Position[] line = functionContext.cfg().getLinePositionMap();
    boolean katch = functionContext.cfg().hasCatchBlock();
    boolean monitor = functionContext.cfg().hasMonitorOp();
    String[] nms = makeNameMap(n, functionContext.entityScopes());

    /*
     * Set reachableBlocks = DFS.getReachableNodes(cfg,
     * Collections.singleton(cfg.entry()));
     * Assertions._assert(reachableBlocks.size() == cfg.getNumberOfNodes(),
     * cfg.toString());
     */

    // (put here to allow subclasses to handle stuff in scoped entities)
    // assemble lexical information
    patchLexicalAccesses(cfg.getInstructions(), functionContext.getAccesses(n));
    AstLexicalInformation LI = new AstLexicalInformation(functionContext.getEntityName(n), (AbstractScope) functionContext.currentScope(), cfg.getInstructions(),
        functionContext.exposeNameSet(n, false), 
        functionContext.exposeNameSet(n, true), 
        functionContext.getAccesses(n));

    DebuggingInformation DBG = new AstDebuggingInformation(n.getPosition(), line, nms);

    // actually make code body
    defineFunction(n, parentContext, cfg, symtab, katch, catchTypes, monitor, LI, DBG);
  }

  protected WalkContext makeLocalContext(WalkContext context, CAstNode n) {
    return new LocalContext((WalkContext) context, makeLocalScope(n, ((WalkContext) context).currentScope()));
  }

  protected WalkContext makeUnwindContext(WalkContext context, CAstNode n, CAstVisitor visitor) {
    // here, n represents the "finally" block of the unwind
    return new UnwindContext(n, (WalkContext) context, visitor);
  }

  private Map> entity2ExposedNames;
  protected int processFunctionExpr(CAstNode n, WalkContext context) {
    CAstEntity fn = (CAstEntity) n.getChild(0).getValue();
    declareFunction(fn, context);
    int result = context.currentScope().allocateTempValue();
    int ex = context.currentScope().allocateTempValue();
    doMaterializeFunction(n, context, result, ex, fn);
    return result;
  }

  protected boolean visitFunctionExpr(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFunctionExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    int result = processFunctionExpr(n, c);
    c.setValue(n, result);
  }

  protected boolean visitFunctionStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFunctionStmt(CAstNode n, WalkContext context, CAstVisitor visitor) {
    int result = processFunctionExpr(n, context);
    CAstEntity fn = (CAstEntity) n.getChild(0).getValue();
    // FIXME: handle redefinitions of functions
    Scope cs = context.currentScope();
    if (cs.contains(fn.getName()) && !cs.isLexicallyScoped(cs.lookup(fn.getName())) && !cs.isGlobal(cs.lookup(fn.getName()))) {
      // if we already have a local with the function's name, write the function
      // value to that local
      assignValue(n, context, cs.lookup(fn.getName()), fn.getName(), result);
    } else if (topLevelFunctionsInGlobalScope() && context.top().getKind() == CAstEntity.SCRIPT_ENTITY) {
      context.getGlobalScope().declare(new FinalCAstSymbol(fn.getName()));
      assignValue(n, context, cs.lookup(fn.getName()), fn.getName(), result);
    } else {
      context.currentScope().declare(new FinalCAstSymbol(fn.getName()), result);
    }
  }

  protected boolean visitLocalScope(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveLocalScope(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(0)));
  }

  protected boolean visitBlockExpr(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(n.getChildCount() - 1)));
  }
  protected boolean visitBlockStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitLoop(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    // loop test block
    context.cfg().newBlock(true);
    PreBasicBlock headerB = context.cfg().getCurrentBlock();
    visitor.visit(n.getChild(0), context, visitor);

    assert c.getValue(n.getChild(0)) != -1 : "error in loop test " + CAstPrinter.print(n.getChild(0), context.top().getSourceMap())
        + " of loop " + CAstPrinter.print(n, context.top().getSourceMap());
    context.cfg().addInstruction(
        insts.ConditionalBranchInstruction(translateConditionOpcode(CAstOperator.OP_EQ), null, c.getValue(n.getChild(0)), context
            .currentScope().getConstantValue(new Integer(0))));
    PreBasicBlock branchB = context.cfg().getCurrentBlock();

    // loop body
    context.cfg().newBlock(true);
    visitor.visit(n.getChild(1), context, visitor);
    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
      context.cfg().addInstruction(insts.GotoInstruction());
      PreBasicBlock bodyB = context.cfg().getCurrentBlock();
      context.cfg().addEdge(bodyB, headerB);

      // next block
      context.cfg().newBlock(false);
    }

    PreBasicBlock nextB = context.cfg().getCurrentBlock();

    // control flow mapping;
  }
    context.cfg().addEdge(branchB, nextB);
    return true;
  }

  // Make final to prevent overriding
  protected final void leaveLoopHeader(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveLoop(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitGetCaughtException(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveGetCaughtException(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    String nm = (String) n.getChild(0).getValue();
    context.currentScope().declare(new FinalCAstSymbol(nm));
    context.cfg().addInstruction(
        insts.GetCaughtExceptionInstruction(context.cfg().getCurrentBlock().getNumber(), context.currentScope().lookup(nm)
            .valueNumber()));
  }

  protected boolean visitThis(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveThis(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, 1);
  }

  protected boolean visitSuper(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveSuper(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, 1);
  }

  protected boolean visitCall(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveCall(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    int exp = context.currentScope().allocateTempValue();
    int fun = c.getValue(n.getChild(0));
    CAstNode functionName = n.getChild(1);
    int[] args = new int[n.getChildCount() - 2];
    for (int i = 0; i < args.length; i++) {

      args[i] = c.getValue(n.getChild(i + 2));
    }
    doCall(context, n, result, exp, functionName, fun, args);
  }

  protected boolean visitVar(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveVar(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    String nm = (String) n.getChild(0).getValue();
    assert nm != null : "cannot find var for " + CAstPrinter.print(n, context.getSourceMap());
    Symbol s = context.currentScope().lookup(nm);
    assert s != null : "cannot find symbol for " + nm + " at " + CAstPrinter.print(n, context.getSourceMap());
    if (context.currentScope().isGlobal(s)) {
      c.setValue(n, doGlobalRead(n, context, nm));
    } else if (context.currentScope().isLexicallyScoped(s)) {
      c.setValue(n, doLexicallyScopedRead(n, context, nm));
    } else {
      c.setValue(n, doLocalRead(context, nm));
    }
  }

  protected boolean visitConstant(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveConstant(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    c.setValue(n, context.currentScope().getConstantValue(n.getValue()));
  }

  protected boolean visitBinaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;

  private boolean handleBinaryOpThrow(CAstNode n, CAstNode op, WalkContext context) {
    // currently, only integer / and % throw exceptions
    boolean mayBeInteger = false;
    Collection labels = context.getControlFlow().getTargetLabels(n);
    if (!labels.isEmpty()) {
      context.cfg().addPreNode(n, context.getUnwindState());

      mayBeInteger = true;
      assert op == CAstOperator.OP_DIV || op == CAstOperator.OP_MOD : CAstPrinter.print(n);
      for (Iterator iter = labels.iterator(); iter.hasNext();) {
        Object label = iter.next();
        CAstNode target = context.getControlFlow().getTarget(n, label);
        if (target == CAstControlFlowMap.EXCEPTION_TO_EXIT)
          context.cfg().addPreEdgeToExit(n, true);
        else
          context.cfg().addPreEdge(n, target, true);
      }
    }

    return mayBeInteger;
  }

  protected void leaveBinaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    CAstNode l = n.getChild(1);
    CAstNode r = n.getChild(2);
    assert c.getValue(r) != -1 : CAstPrinter.print(n);
    assert c.getValue(l) != -1 : CAstPrinter.print(n);

    boolean mayBeInteger = handleBinaryOpThrow(n, n.getChild(0), context);

    context.cfg().addInstruction(
        insts.BinaryOpInstruction(translateBinaryOpcode(n.getChild(0)), false, false, result, c.getValue(l), c.getValue(r),
            mayBeInteger));

    if (mayBeInteger) {
      context.cfg().newBlock(true);
    }
  }

  protected boolean visitUnaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveUnaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    CAstNode v = n.getChild(1);
    context.cfg().addInstruction(insts.UnaryOpInstruction(translateUnaryOpcode(n.getChild(0)), result, c.getValue(v)));
  }
  protected boolean visitArrayLength(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveArrayLength(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    int arrayValue = c.getValue(n.getChild(0));
    context.cfg().addInstruction(insts.ArrayLengthInstruction(result, arrayValue));
  }

  protected boolean visitArrayRef(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveArrayRef(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int arrayValue = c.getValue(n.getChild(0));
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    arrayOpHandler.doArrayRead(context, result, arrayValue, n, gatherArrayDims(c, n));
  }

  protected boolean visitDeclStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  // TODO: should we handle exploded declaration nodes here instead?
  protected void leaveDeclStmt(CAstNode n, WalkContext c, CAstVisitor visitor) {
    CAstSymbol s = (CAstSymbol) n.getChild(0).getValue();
    String nm = s.name();
    Scope scope = c.currentScope();
    if (n.getChildCount() == 2) {
      CAstNode v = n.getChild(1);
      if (scope.contains(nm) && scope.lookup(nm).getDefiningScope() == scope) {
        assert !s.isFinal();
        doLocalWrite(c, nm, c.getValue(v));
      } else if (v.getKind() != CAstNode.CONSTANT && v.getKind() != CAstNode.VAR && v.getKind() != CAstNode.THIS) {
        scope.declare(s, c.getValue(v));
      } else {
        scope.declare(s);
        doLocalWrite(c, nm, c.getValue(v));
      }
    } else {
      c.currentScope().declare(s);
    }
  }

  protected boolean visitReturn(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveReturn(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (n.getChildCount() > 0) {
      context.cfg().addInstruction(insts.ReturnInstruction(c.getValue(n.getChild(0)), false));
    } else {
      context.cfg().addInstruction(insts.ReturnInstruction());
    }

    context.cfg().addPreNode(n, context.getUnwindState());
    context.cfg().newBlock(false);
    context.cfg().addPreEdgeToExit(n, false);
  }

  protected boolean visitIfgoto(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveIfgoto(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (n.getChildCount() == 1) {
      context.cfg().addInstruction(
          insts.ConditionalBranchInstruction(translateConditionOpcode(CAstOperator.OP_NE), null, c.getValue(n.getChild(0)), context
              .currentScope().getConstantValue(new Integer(0))));
    } else if (n.getChildCount() == 3) {
      context.cfg().addInstruction(
          insts.ConditionalBranchInstruction(translateConditionOpcode(n.getChild(0)), null, c.getValue(n.getChild(1)),
              c.getValue(n.getChild(2))));
    } else {
      Assertions.UNREACHABLE();
    }

    context.cfg().addPreNode(n, context.getUnwindState());
    context.cfg().newBlock(true);
    context.cfg().addPreEdge(n, context.getControlFlow().getTarget(n, Boolean.TRUE), false);
  }


    }
  protected boolean visitGoto(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveGoto(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
      context.cfg().addPreNode(n, context.getUnwindState());
      context.cfg().addInstruction(insts.GotoInstruction());
      context.cfg().newBlock(false);
      if (context.getControlFlow().getTarget(n, null) == null) {
        assert context.getControlFlow().getTarget(n, null) != null : context.getControlFlow() + " does not map " + n + " ("
            + context.getSourceMap().getPosition(n) + ")";
      }
      context.cfg().addPreEdge(n, context.getControlFlow().getTarget(n, null), false);
    }
  }

  protected boolean visitLabelStmt(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (!context.getControlFlow().getSourceNodes(n).isEmpty()) {
      context.cfg().newBlock(true);
      context.cfg().addPreNode(n, context.getUnwindState());
    }
    return false;
  }

  protected void leaveLabelStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected void processIf(CAstNode n, boolean isExpr, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    PreBasicBlock trueB = null, falseB = null;
    // conditional
    CAstNode l = n.getChild(0);
    visitor.visit(l, context, visitor);
    context.cfg().addInstruction(

        insts.ConditionalBranchInstruction(translateConditionOpcode(CAstOperator.OP_EQ), null, c.getValue(l), context.currentScope()
            .getConstantValue(new Integer(0))));
    PreBasicBlock srcB = context.cfg().getCurrentBlock();
    // true clause
    context.cfg().newBlock(true);
    CAstNode r = n.getChild(1);
    visitor.visit(r, context, visitor);
    if (isExpr)
      context.cfg().addInstruction(new AssignInstruction(c.getValue(n), c.getValue(r)));
    if (n.getChildCount() == 3) {
      if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
        context.cfg().addInstruction(insts.GotoInstruction());
        trueB = context.cfg().getCurrentBlock();

        // false clause
        context.cfg().newBlock(false);
      }

      falseB = context.cfg().getCurrentBlock();
      CAstNode f = n.getChild(2);
      visitor.visit(f, context, visitor);
      if (isExpr)
        context.cfg().addInstruction(new AssignInstruction(c.getValue(n), c.getValue(f)));
    }

    // end
    context.cfg().newBlock(true);
    if (n.getChildCount() == 3) {
      if (trueB != null)
        context.cfg().addEdge(trueB, context.cfg().getCurrentBlock());
      context.cfg().addEdge(srcB, falseB);
    } else {
      context.cfg().addEdge(srcB, context.cfg().getCurrentBlock());
    }
  }

  // Make final to prevent overriding
  protected final void leaveIfStmtCondition(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfStmtTrueClause(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfExprCondition(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfExprTrueClause(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfExpr(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }
  protected boolean visitIfStmt(CAstNode n, WalkContext c, CAstVisitor visitor) {
    processIf(n, false, c, visitor);
    return true;
  }

  protected boolean visitIfExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    processIf(n, true, c, visitor);
    return true;
  }

  protected boolean visitNew(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveNew(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;

    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);

    int[] arguments;
    if (n.getChildCount() <= 1) {
      arguments = null;
    } else {
      arguments = new int[n.getChildCount() - 1];
      for (int i = 1; i < n.getChildCount(); i++) {
        arguments[i - 1] = c.getValue(n.getChild(i));
      }
    }
    doNewObject(context, n, result, n.getChild(0).getValue(), arguments);
  }

  protected boolean visitObjectLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveObjectLiteralFieldInit(CAstNode n, int i, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (n.getChild(i).getKind() == CAstNode.EMPTY) {
      handleUnspecifiedLiteralKey(context, n, i, visitor);
    }
    doFieldWrite(context, c.getValue(n.getChild(0)), n.getChild(i), n, c.getValue(n.getChild(i + 1)));
  }

  protected void leaveObjectLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(0)));
  }

  protected boolean visitArrayLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveArrayLiteralObject(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(0)));
  }

  protected void leaveArrayLiteralInitElement(CAstNode n, int i, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    arrayOpHandler.doArrayWrite(context, c.getValue(n.getChild(0)), n,
        new int[] { context.currentScope().getConstantValue(new Integer(i - 1)) }, c.getValue(n.getChild(i)));
  }

  protected void leaveArrayLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitObjectRef(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveObjectRef(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    CAstNode elt = n.getChild(1);
    doFieldRead(context, result, c.getValue(n.getChild(0)), elt, n);
  }

  public boolean visitAssign(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  public void leaveAssign(CAstNode n, WalkContext c, CAstVisitor visitor) {
    if (n.getKind() == CAstNode.ASSIGN) {
      c.setValue(n, c.getValue(n.getChild(1)));
    } else {
      c.setValue(n, c.getValue(n.getChild(0)));
    }
  }

  private int[] gatherArrayDims(WalkContext c, CAstNode n) {
    int numDims = n.getChildCount() - 2;
    int[] dims = new int[numDims];
    for (int i = 0; i < numDims; i++)
      dims[i] = c.getValue(n.getChild(i + 2));
    return dims;
  }

  /* Prereq: a.getKind() == ASSIGN_PRE_OP || a.getKind() == ASSIGN_POST_OP */
  protected int processAssignOp(CAstNode n, CAstNode v, CAstNode a, int temp, boolean post, WalkContext c) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    CAstNode op = a.getChild(2);
    int temp2 = context.currentScope().allocateTempValue();

    boolean mayBeInteger = handleBinaryOpThrow(a, op, context);

    context.cfg().addInstruction(
        insts.BinaryOpInstruction(translateBinaryOpcode(op), false, false, temp2, temp, rval, mayBeInteger));

    if (mayBeInteger) {
      context.cfg().newBlock(true);
    }

    return temp2;
  }

  protected boolean visitArrayRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveArrayRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    c.setValue(n, rval);
    arrayOpHandler.doArrayWrite(context, c.getValue(n.getChild(0)), n, gatherArrayDims(c, n), rval);
  }

  protected boolean visitArrayRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }
  protected void leaveArrayRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int temp = context.currentScope().allocateTempValue();
    int[] dims = gatherArrayDims(c, n);
    arrayOpHandler.doArrayRead(context, temp, c.getValue(n.getChild(0)), n, dims);
    int rval = processAssignOp(n, v, a, temp, !pre, c);
    c.setValue(n, pre ? rval : temp);
    arrayOpHandler.doArrayWrite(context, c.getValue(n.getChild(0)), n, dims, rval);
  }

  protected boolean visitObjectRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveObjectRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    c.setValue(n, rval);
    doFieldWrite(context, c.getValue(n.getChild(0)), n.getChild(1), n, rval);
  }

  protected void processObjectRefAssignOp(CAstNode n, CAstNode v, CAstNode a, WalkContext c) {
  }

  protected boolean visitObjectRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveObjectRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int temp = context.currentScope().allocateTempValue();
    doFieldRead(context, temp, c.getValue(n.getChild(0)), n.getChild(1), n);
    int rval = processAssignOp(n, v, a, temp, !pre, c);
    c.setValue(n, pre ? rval : temp);
    doFieldWrite(context, c.getValue(n.getChild(0)), n.getChild(1), n, rval);
  }

  protected boolean visitBlockExprAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockExprAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(n.getChildCount() - 1)));
  }

  protected boolean visitBlockExprAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockExprAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    c.setValue(n, c.getValue(n.getChild(n.getChildCount() - 1)));
  }

  protected boolean visitVarAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  /**
   * assign rval to nm as appropriate, depending on the scope of ls
   */
  protected void assignValue(CAstNode n, WalkContext context, Symbol ls, String nm, int rval) {
    if (context.currentScope().isGlobal(ls))
      doGlobalWrite(context, nm, rval);
    else if (context.currentScope().isLexicallyScoped(ls)) {
      doLexicallyScopedWrite(context, nm, rval);
    } else {
      assert rval != -1 : CAstPrinter.print(n, context.top().getSourceMap());
      doLocalWrite(context, nm, rval);
    }
  }

  protected void leaveVarAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    String nm = (String) n.getChild(0).getValue();
    Symbol ls = context.currentScope().lookup(nm);
    c.setValue(n, rval);
    assignValue(n, context, ls, nm, rval);
  }

  protected boolean visitVarAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveVarAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    String nm = (String) n.getChild(0).getValue();
    Symbol ls = context.currentScope().lookup(nm);
    int temp;

    if (context.currentScope().isGlobal(ls))
      temp = doGlobalRead(n, context, nm);
    else if (context.currentScope().isLexicallyScoped(ls)) {
      temp = doLexicallyScopedRead(n, context, nm);
    } else {
      temp = doLocalRead(context, nm);
    }

    if (!pre) {
      int ret = context.currentScope().allocateTempValue();
      context.cfg().addInstruction(new AssignInstruction(ret, temp));
      c.setValue(n, ret);
    }

    int rval = processAssignOp(n, v, a, temp, !pre, c);

    if (pre) {
      c.setValue(n, rval);
    }

    if (context.currentScope().isGlobal(ls)) {
      doGlobalWrite(context, nm, rval);
    } else if (context.currentScope().isLexicallyScoped(ls)) {
      doLexicallyScopedWrite(context, nm, rval);
    } else {
      doLocalWrite(context, nm, rval);
    }
  }

  private boolean isSimpleSwitch(CAstNode n, WalkContext context, CAstVisitor visitor) {
    CAstControlFlowMap ctrl = context.getControlFlow();
    Collection caseLabels = ctrl.getTargetLabels(n);
    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();

      if (x == CAstControlFlowMap.SWITCH_DEFAULT)
        continue;

      CAstNode xn = (CAstNode) x;
      if (xn.getKind() == CAstNode.CONSTANT) {
        visitor.visit(xn, context, visitor);
        if (context.getValue(xn) != -1) {
          if (context.currentScope().isConstant(context.getValue(xn))) {
            Object val = context.currentScope().getConstantObject(context.getValue(xn));
            if (val instanceof Number) {
              Number num = (Number) val;
              if ((double) num.intValue() == num.doubleValue()) {
                continue;
              }
            }
          }
        }
      }

      return false;

    return true;
  }

  private void doSimpleSwitch(CAstNode n, WalkContext context, CAstVisitor visitor) {
    PreBasicBlock defaultHackBlock = null;
    CAstControlFlowMap ctrl = context.getControlFlow();

    CAstNode switchValue = n.getChild(0);
    visitor.visit(switchValue, context, visitor);
    int v = context.getValue(switchValue);

    boolean hasExplicitDefault = ctrl.getTarget(n, CAstControlFlowMap.SWITCH_DEFAULT) != null;

    Collection caseLabels = ctrl.getTargetLabels(n);
    int cases = caseLabels.size();
    if (hasExplicitDefault)
      cases--;
    int[] casesAndLabels = new int[cases * 2];

    int defaultBlock = context.cfg().getCurrentBlock().getGraphNodeId() + 1;

    context.cfg().addInstruction(insts.SwitchInstruction(v, defaultBlock, casesAndLabels));
    context.cfg().addPreNode(n, context.getUnwindState());
    // PreBasicBlock switchB = context.cfg().getCurrentBlock();
    context.cfg().newBlock(true);

    context.cfg().addInstruction(insts.GotoInstruction());
    defaultHackBlock = context.cfg().getCurrentBlock();
    context.cfg().newBlock(false);

    CAstNode switchBody = n.getChild(1);
    visitor.visit(switchBody, context, visitor);
    context.cfg().newBlock(true);

    if (!hasExplicitDefault) {
      context.cfg().addEdge(defaultHackBlock, context.cfg().getCurrentBlock());
    }

    int cn = 0;

    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();
      CAstNode target = ctrl.getTarget(n, x);
      if (x == CAstControlFlowMap.SWITCH_DEFAULT) {
        context.cfg().addEdge(defaultHackBlock, context.cfg().getBlock(target));
      } else {
        Number caseLabel = (Number) context.currentScope().getConstantObject(context.getValue((CAstNode) x));
        casesAndLabels[2 * cn] = caseLabel.intValue();
        casesAndLabels[2 * cn + 1] = context.cfg().getBlock(target).getGraphNodeId();
        cn++;

        context.cfg().addPreEdge(n, target, false);
      }
    }
  }

  private void doIfConvertSwitch(CAstNode n, WalkContext context, CAstVisitor visitor) {
    CAstControlFlowMap ctrl = context.getControlFlow();
    context.cfg().addPreNode(n, context.getUnwindState());

    CAstNode switchValue = n.getChild(0);
    visitor.visit(switchValue, context, visitor);
    int v = context.getValue(switchValue);

    Collection caseLabels = ctrl.getTargetLabels(n);
    Map labelToBlock = new LinkedHashMap();
    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();
      if (x != CAstControlFlowMap.SWITCH_DEFAULT) {
        visitor.visit((CAstNode) x, context, visitor);
        context.cfg().addInstruction(
            insts.ConditionalBranchInstruction(translateConditionOpcode(CAstOperator.OP_EQ), null, v, context.getValue((CAstNode) x)));
        labelToBlock.put(x, context.cfg().getCurrentBlock());
        context.cfg().newBlock(true);
      }
    }

    PreBasicBlock defaultGotoBlock = context.cfg().getCurrentBlock();
    context.cfg().addInstruction(insts.GotoInstruction());
    context.cfg().newBlock(false);

    CAstNode switchBody = n.getChild(1);
    visitor.visit(switchBody, context, visitor);
    context.cfg().newBlock(true);

    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();
      if (x != CAstControlFlowMap.SWITCH_DEFAULT) {
        CAstNode target = ctrl.getTarget(n, x);
        context.cfg().addEdge(labelToBlock.get(x), context.cfg().getBlock(target));
      }
    }

    if (ctrl.getTarget(n, CAstControlFlowMap.SWITCH_DEFAULT) == null) {
      context.cfg().addEdge(defaultGotoBlock, context.cfg().getCurrentBlock());
    } else {
      CAstNode target = ctrl.getTarget(n, CAstControlFlowMap.SWITCH_DEFAULT);
      context.cfg().addEdge(defaultGotoBlock, context.cfg().getBlock(target));
    }
  }

  protected boolean visitSwitch(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (isSimpleSwitch(n, context, visitor)) {
      doSimpleSwitch(n, context, visitor);
    } else {
      doIfConvertSwitch(n, context, visitor);
    }
    return true;
  }

  // Make final to prevent overriding
  protected final void leaveSwitchValue(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveSwitch(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitThrow(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveThrow(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    doThrow(context, c.getValue(n.getChild(0)));

    context.cfg().addPreNode(n, context.getUnwindState());
    context.cfg().newBlock(false);

    Collection labels = context.getControlFlow().getTargetLabels(n);
    for (Iterator iter = labels.iterator(); iter.hasNext();) {
      Object label = iter.next();
      CAstNode target = context.getControlFlow().getTarget(n, label);
      if (target == CAstControlFlowMap.EXCEPTION_TO_EXIT)
        context.cfg().addPreEdgeToExit(n, true);
      else
        context.cfg().addPreEdge(n, target, true);
    }
  }

  protected boolean visitCatch(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;

    // unreachable catch block
    if (context.getControlFlow().getSourceNodes(n).isEmpty()) {
      return true;
    }

    String id = (String) n.getChild(0).getValue();
    context.cfg().setCurrentBlockAsHandler();
    if (!context.currentScope().contains(id)) {
      context.currentScope().declare(new FinalCAstSymbol(id));
    }
    context.cfg().addInstruction(
        insts.GetCaughtExceptionInstruction(context.cfg().getCurrentBlock().getNumber(), context.currentScope().lookup(id)
            .valueNumber()));

    context.cfg().addPreNode(n, context.getUnwindState());

    CAstType caughtType = getTypeForNode(context, n);
    if (caughtType != null) {
      TypeReference caughtRef = makeType(caughtType);
      context.setCatchType(n, caughtRef);
    } else {
      context.setCatchType(n, defaultCatchType());
    }

    return false;
  }

  protected void leaveCatch(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitUnwind(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveUnwind(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  private boolean hasIncomingEdges(CAstNode n, WalkContext context) {
    if (context.cfg().hasDelayedEdges(n)) {
      return true;
    } else {
      for (int i = 0; i < n.getChildCount(); i++) {
        if (hasIncomingEdges(n.getChild(i), context)) {
          return true;
        }
      }

      return false;
    }
  }

  protected boolean visitTry(final CAstNode n, WalkContext c, CAstVisitor visitor) {
    final WalkContext context = (WalkContext) c;
    boolean addSkipCatchGoto = false;
    visitor.visit(n.getChild(0), context, visitor);
    PreBasicBlock endOfTry = context.cfg().getCurrentBlock();

    if (!hasIncomingEdges(n.getChild(1), context)) {
      if (loader instanceof CAstAbstractLoader) {
        ((CAstAbstractLoader) loader).addMessage(context.getModule(), new Warning(Warning.MILD) {
          @Override
          public String getMsg() {
            return "Dead catch block at " + getPosition(context.getSourceMap(), n.getChild(1));
          }
        });
      }
      return true;
    }

    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
      addSkipCatchGoto = true;
      context.cfg().addInstruction(insts.GotoInstruction());
      context.cfg().newBlock(false);
    }

    context.cfg().noteCatchBlock();
    visitor.visit(n.getChild(1), context, visitor);

    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
      context.cfg().newBlock(true);
    }

    if (addSkipCatchGoto) {
      PreBasicBlock afterBlock = context.cfg().getCurrentBlock();
      context.cfg().addEdge(endOfTry, afterBlock);
    }
    return true;
  }

  // Make final to prevent overriding
    if (DEBUG_TOP)
  protected final void leaveTryBlock(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveTry(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitEmpty(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveEmpty(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    c.setValue(n, context.currentScope().getConstantValue(null));
  }

  protected boolean visitPrimitive(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leavePrimitive(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);

    doPrimitive(result, context, n);
  }

  protected boolean visitVoid(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveVoid(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, -1);
  }

  protected boolean visitAssert(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveAssert(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    boolean fromSpec = true;
    int result = c.getValue(n.getChild(0));
    if (n.getChildCount() == 2) {
      assert n.getChild(1).getKind() == CAstNode.CONSTANT;
      assert n.getChild(1).getValue() instanceof Boolean;
      fromSpec = n.getChild(1).getValue().equals(Boolean.TRUE);
    }
    context.cfg().addInstruction(new AstAssertInstruction(result, fromSpec));
  }

  protected boolean visitEachElementGet(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

          } else if (expr instanceof CAstOperator) {
  protected void leaveEachElementGet(CAstNode n, WalkContext c, CAstVisitor visitor) {
    int result = ((WalkContext) c).currentScope().allocateTempValue();
    c.setValue(n, result);
    ((WalkContext) c).cfg().addInstruction(new EachElementGetInstruction(result, c.getValue(n.getChild(0))));
  }

  protected boolean visitEachElementHasNext(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  @Override
  protected void leaveEachElementHasNext(CAstNode n, WalkContext c, CAstVisitor visitor) {
    int result = ((WalkContext) c).currentScope().allocateTempValue();
    c.setValue(n, result);
    ((WalkContext) c).cfg().addInstruction(new EachElementHasNextInstruction(result, c.getValue(n.getChild(0))));
  }

  protected boolean visitTypeLiteralExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  protected void leaveTypeLiteralExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;
    assert n.getChild(0).getKind() == CAstNode.CONSTANT;
    String typeNameStr = (String) n.getChild(0).getValue();
    TypeName typeName = TypeName.string2TypeName(typeNameStr);
    TypeReference typeRef = TypeReference.findOrCreate(loader.getReference(), typeName);

    int result = wc.currentScope().allocateTempValue();
    c.setValue(n, result);

    wc.cfg().addInstruction(insts.LoadMetadataInstruction(result, loader.getLanguage().getConstantType(typeRef), typeRef));
  }

  protected boolean visitIsDefinedExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  protected void leaveIsDefinedExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;
    int ref = c.getValue(n.getChild(0));
    int result = wc.currentScope().allocateTempValue();
    c.setValue(n, result);
    if (n.getChildCount() == 1) {
      wc.cfg().addInstruction(new AstIsDefinedInstruction(result, ref));
    } else {
      doIsFieldDefined(wc, result, ref, n.getChild(1));
    }
  }

  protected boolean visitEcho(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  protected void leaveEcho(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;

    int rvals[] = new int[n.getChildCount()];
    for (int i = 0; i < n.getChildCount(); i++) {
      rvals[i] = c.getValue(n.getChild(i));
    }

    wc.cfg().addInstruction(new AstEchoInstruction(rvals));
  }

  public CAstEntity getIncludedEntity(CAstNode n) {
    if (n.getChild(0).getKind() == CAstNode.NAMED_ENTITY_REF) {
      assert namedEntityResolver != null;
      return (CAstEntity) namedEntityResolver.get(n.getChild(0).getChild(0).getValue());
    } else {
      return (CAstEntity) n.getChild(0).getValue();
    }
  }

  protected void leaveInclude(final CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;

    CAstEntity included = getIncludedEntity(n);

    if (included == null) {
      System.err.println(("cannot find include for " + CAstPrinter.print(n)));
      System.err.println(("from:\n" + namedEntityResolver));
    } else {
      final boolean isMacroExpansion = (included.getKind() == CAstEntity.MACRO_ENTITY);

      System.err.println("found " + included.getName() + " for " + CAstPrinter.print(n));

      final CAstEntity copy = (new CAstCloner(new CAstImpl(), true) {

        private CAstNode copyIncludeExpr(CAstNode expr) {
          if (expr.getValue() != null) {
            return Ast.makeConstant(expr.getValue());
    }
            return expr;
          } else {
            CAstNode nc[] = new CAstNode[expr.getChildCount()];

            for (int i = 0; i < expr.getChildCount(); i++) {
              nc[i] = copyIncludeExpr(expr.getChild(i));
            }

            return Ast.makeNode(expr.getKind(), nc);
          }
        }

        protected CAstNode copyNodes(CAstNode root, final CAstControlFlowMap cfg, NonCopyingContext c, Map, CAstNode> nodeMap) {
          if (isMacroExpansion && root.getKind() == CAstNode.MACRO_VAR) {
            int arg = ((Number) root.getChild(0).getValue()).intValue();
            CAstNode expr = copyIncludeExpr(n.getChild(arg));
            nodeMap.put(Pair.make(root, c.key()), expr);
            return expr;
          } else {
            return super.copyNodesHackForEclipse(root, cfg, c, nodeMap);
          }
        }
      }).rewrite(included);

      if (copy.getAST() == null) {
        System.err.println((copy.getName() + " has no AST"));

      } else {
        visit(copy.getAST(), new DelegatingContext(wc) {
          public CAstSourcePositionMap getSourceMap() {
            return copy.getSourceMap();
          }

          public CAstControlFlowMap getControlFlow() {
            return copy.getControlFlow();
          }
        }, visitor);

        visitor.visitScopedEntities(copy, copy.getAllScopedEntities(), wc, visitor);
      }
    }
  }
  protected final void walkEntities(CAstEntity N, WalkContext c) {
    visitEntities(N, c, this);
  }

  public final class RootContext implements WalkContext {
    private final Scope globalScope;
    
    private final CAstEntity N;

    private final ModuleEntry module;

    private final Map entityNames = new LinkedHashMap();

   public RootContext(CAstEntity N, ModuleEntry module) {
      this.N = N;
      this.module = module;
      this.globalScope = makeGlobalScope();
    }

    public ModuleEntry getModule() {
      return module;
    }

    public String file() {
      return module.getName();
    }

    public CAstEntity top() {
      return N;
    }

    public Scope currentScope() {
      return globalScope;
    }

    public Set entityScopes() {
      return Collections.singleton(globalScope);
    }

    public CAstSourcePositionMap getSourceMap() {
      return N.getSourceMap();
    }

    public CAstControlFlowMap getControlFlow() {
      return N.getControlFlow();
    }

    public IncipientCFG cfg() {
      return null;
    }

    public UnwindState getUnwindState() {
      return null;
    }

    public String getName() {
      return null;
    }

    public void setCatchType(int blockNumber, TypeReference catchType) {
    }

    public void setCatchType(CAstNode castNode, TypeReference catchType) {
    }

    public TypeReference[][] getCatchTypes() {
      return null;
    
    public void addEntityName(CAstEntity e, String name) {
      entityNames.put(e, name);
    }

    public String getEntityName(CAstEntity e) {
      if (e == null) {
        return null;
      } else {
        assert entityNames.containsKey(e);
        return "L" + entityNames.get(e);
      }
    }

    public boolean hasValue(CAstNode n) {
      assert false;
      return false;
    }

    public int setValue(CAstNode n, int v) {
      assert false;
      return 0;
    }

    public int getValue(CAstNode n) {
      assert false;
      return -1;
    }

    public Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet) {
      assert false;
      return null;
    }

    public Set getAccesses(CAstEntity e) {
      assert false;
      return null;
    }

    public Scope getGlobalScope(){
      return globalScope;
    }
    
  };

  /**
   * translate module, represented by {@link CAstEntity} N
   */
  public void translate(final CAstEntity N, final ModuleEntry module) {
      System.err.println(("translating " + module.getName()));
    // this.inlinedSourceMap = inlinedSourceMap;
    final ExposedNamesCollector exposedNamesCollector = new ExposedNamesCollector();
    exposedNamesCollector.run(N);
    entity2ExposedNames = exposedNamesCollector.getEntity2ExposedNames();
    // CAstEntity rewrite = (new ExposedParamRenamer(new CAstImpl(),
    // entity2ExposedNames)).rewrite(N);
    walkEntities(N, new RootContext(N, module));
  }

  public void translate(final CAstEntity N, final WalkContext context) {
    final ExposedNamesCollector exposedNamesCollector = new ExposedNamesCollector();
    exposedNamesCollector.run(N);
    entity2ExposedNames = exposedNamesCollector.getEntity2ExposedNames();
    walkEntities(N, context);
  }
 
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
    return false;
/******************************************************************************
 * Copyright (c) 2002 - 2006 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.cast.ir.translator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.cast.ir.ssa.AssignInstruction;
import com.ibm.wala.cast.ir.ssa.AstAssertInstruction;
import com.ibm.wala.cast.ir.ssa.AstConstants;
import com.ibm.wala.cast.ir.ssa.AstEchoInstruction;
import com.ibm.wala.cast.ir.ssa.AstGlobalRead;
import com.ibm.wala.cast.ir.ssa.AstGlobalWrite;
import com.ibm.wala.cast.ir.ssa.AstIsDefinedInstruction;
import com.ibm.wala.cast.ir.ssa.AstLexicalAccess;
import com.ibm.wala.cast.ir.ssa.AstLexicalAccess.Access;
import com.ibm.wala.cast.ir.ssa.AstLexicalRead;
import com.ibm.wala.cast.ir.ssa.AstLexicalWrite;
import com.ibm.wala.cast.ir.ssa.EachElementGetInstruction;
import com.ibm.wala.cast.ir.ssa.EachElementHasNextInstruction;
import com.ibm.wala.cast.ir.ssa.SSAConversion;
import com.ibm.wala.cast.loader.AstMethod.DebuggingInformation;
import com.ibm.wala.cast.loader.AstMethod.LexicalInformation;
import com.ibm.wala.cast.loader.CAstAbstractLoader;
import com.ibm.wala.cast.tree.CAstControlFlowMap;
import com.ibm.wala.cast.tree.CAstEntity;
import com.ibm.wala.cast.tree.CAstNode;
import com.ibm.wala.cast.tree.CAstSourcePositionMap;
import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
import com.ibm.wala.cast.tree.CAstSymbol;
import com.ibm.wala.cast.tree.CAstType;
import com.ibm.wala.cast.tree.impl.CAstImpl;
   */
import com.ibm.wala.cast.tree.impl.CAstOperator;
import com.ibm.wala.cast.tree.impl.CAstSymbolImpl;
import com.ibm.wala.cast.tree.impl.CAstSymbolImplBase;
import com.ibm.wala.cast.tree.rewrite.CAstCloner;
import com.ibm.wala.cast.tree.rewrite.CAstRewriter;
import com.ibm.wala.cast.tree.visit.CAstVisitor;
import com.ibm.wala.cast.types.AstTypeReference;
import com.ibm.wala.cast.util.CAstPrinter;
import com.ibm.wala.cfg.AbstractCFG;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.IClassLoader;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.ModuleEntry;
import com.ibm.wala.shrikeBT.BinaryOpInstruction;
import com.ibm.wala.shrikeBT.ConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.ShiftInstruction;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAMonitorInstruction;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.INodeWithNumber;
import com.ibm.wala.util.graph.impl.SparseNumberedGraph;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.warnings.Warning;

/**
 * Common code to translate CAst to IR. Must be specialized by each language to
 * handle semantics appropriately.
 */
public abstract class AstTranslator extends CAstVisitor implements ArrayOpHandler, TranslatorToIR {

  /**
   * set to true to use new handling of lexical scoping
   */
  public static final boolean NEW_LEXICAL = true;


  /**
   * does the language care about using type-appropriate default values? For
   * Java, the answer is yes (ints should get a default value of 0, null for
   * pointers, etc.). For JavaScript, the answer is no, as any variable can hold
   * the value 'undefined'.
   */
  protected abstract boolean useDefaultInitValues();

  /**
   * can lexical reads / writes access globals?
   */
  protected abstract boolean treatGlobalsAsLexicallyScoped();

  /**
   * given accesses in a method to variables defined in an enclosing lexical
   * scope, is it legal to read the variable into a local l once at the
   * beginning of the method, operate on l through the method body (rather than
   * performing separate lexical read / write operations), and write back the
   * value in l (if necessary) at the end of the method?
   */
  protected abstract boolean useLocalValuesForLexicalVars();

  protected boolean topLevelFunctionsInGlobalScope() {
    return true;
  }

  /**
   * for a block that catches all exceptions, what is the root exception type
   * that it can catch? E.g., for Java, java.lang.Throwable
   */
  protected abstract TypeReference defaultCatchType();

  protected abstract TypeReference makeType(CAstType type);

  /**
   * define a new (presumably nested) type. return true if type was successfully
   * defined, false otherwise
   */
  protected abstract boolean defineType(CAstEntity type, WalkContext wc);

  /**
   * declare a new function, represented by N
  protected abstract void declareFunction(CAstEntity N, WalkContext context);

  /**
   * fully define a function. invoked after all the code of the function has
   * been processed
   */
  protected abstract void defineFunction(CAstEntity N, WalkContext definingContext, AbstractCFG cfg, SymbolTable symtab,
      boolean hasCatchBlock, TypeReference[][] caughtTypes, boolean hasMonitorOp, AstLexicalInformation lexicalInfo,
      DebuggingInformation debugInfo);

  /**
   * define a new field fieldEntity within topEntity
   */
  protected abstract void defineField(CAstEntity topEntity, WalkContext context, CAstEntity fieldEntity);

  /**
   * create the language-appropriate name for f
   */
  protected abstract String composeEntityName(WalkContext parent, CAstEntity f);

  /**
   * generate IR for a CAst throw expression, updating context.cfg()
   */
  protected abstract void doThrow(WalkContext context, int exception);

  /**
   * generate IR for a CAst array read, updating context.cfg()
   */
  public abstract void doArrayRead(WalkContext context, int result, int arrayValue, CAstNode arrayRef, int[] dimValues);

  /**
   * generate IR for a CAst array write, updating context.cfg()
   */
  public abstract void doArrayWrite(WalkContext context, int arrayValue, CAstNode arrayRef, int[] dimValues, int rval);

  /**
   * generate IR for a CAst field read, updating context.cfg()
   */
  protected abstract void doFieldRead(WalkContext context, int result, int receiver, CAstNode elt, CAstNode parent);

  /**
   * generate IR for a CAst field write, updating context.cfg()
   */
  protected abstract void doFieldWrite(WalkContext context, int receiver, CAstNode elt, CAstNode parent, int rval);

  /**
   * generate IR for a CAst function expression, updating context.cfg()
   */
  protected abstract void doMaterializeFunction(CAstNode node, WalkContext context, int result, int exception, CAstEntity fn);

  /**
   * generate IR for a CAst new expression, updating context.cfg()
   */
  protected abstract void doNewObject(WalkContext context, CAstNode newNode, int result, Object type, int[] arguments);

  /**
   * generate IR for a CAst method call expression, updating context.cfg()
   */
  protected abstract void doCall(WalkContext context, CAstNode call, int result, int exception, CAstNode name, int receiver,
      int[] arguments);

  /**
   * used to generate instructions for array operations; defaults to this
   */
  private ArrayOpHandler arrayOpHandler;

  protected boolean isExceptionLabel(Object label) {
    if (label == null)
      return false;
    if (label instanceof Boolean)
      return false;
    if (label instanceof Number)
      return false;
    if (label == CAstControlFlowMap.SWITCH_DEFAULT)
      return false;
    return true;
  }

  /**
   * If this returns true, new global declarations get created for any attempt
   * to access a non-existent variable (believe it or not, JavaScript actually
   * does this!)
   */
  protected boolean hasImplicitGlobals() {
  }

  /**
   * If this returns true, then attempts to lookup non-existent names return
   * `null' rather than tripping an assertion. This can be used when special
   * handling is needed for built-in names. (PHP does this)
   */
  protected boolean hasSpecialUndeclaredVariables() {
    return false;
  }

  /**
   * some languages let you omit initialization of certain fields when writing
   * an object literal (e.g., PHP). This method should be overridden to handle
   * such cases.
   */
  protected void handleUnspecifiedLiteralKey(WalkContext context, CAstNode objectLiteralNode, int unspecifiedLiteralIndex,
      CAstVisitor visitor) {
    Assertions.UNREACHABLE();
  }

  /**
   * generate prologue code for each function body
   */
  protected void doPrologue(WalkContext context) {
    // if we are SSA converting lexical accesses, add a placeholder instruction
    // eventually (via mutation of its Access array) reads all relevant lexical
    // variables at the beginning of the method.
    if (useLocalValuesForLexicalVars()) {
      context.cfg().addInstruction(new AstLexicalRead(context.cfg().currentInstruction, new Access[0]));
    } else {
      // perform a lexical write to copy the value stored in the local
      // associated with each parameter to the lexical name
      final CAstEntity entity = context.top();
      Set exposedNames = entity2ExposedNames.get(entity);
      if (exposedNames != null) {
        for (String arg : entity.getArgumentNames()) {
          if (exposedNames.contains(arg)) {
            final Scope currentScope = context.currentScope();
            Symbol symbol = currentScope.lookup(arg);
            assert symbol.getDefiningScope() == currentScope;
            int argVN = symbol.valueNumber();
            Access A = new Access(arg, context.getEntityName(entity), argVN);
            context.cfg().addInstruction(new AstLexicalWrite(context.cfg().currentInstruction, A));
          }
        }
      }
    }
  }

  /**
   * generate IR for call modeling creation of primitive value, updating
   * context.cfg()
   */
  protected abstract void doPrimitive(int resultVal, WalkContext context, CAstNode primitiveCall);

  /**
   * get the value number for a name defined locally (i.e., within the current
   * method) by looking up the name in context.currentScope(). Note that the
   * caller is responsible for ensuring that name is defined in the local scope.
   */
  protected int doLocalRead(WalkContext context, String name) {
    if (!useLocalValuesForLexicalVars()) {
      CAstEntity entity = context.top();
      Set exposed = entity2ExposedNames.get(entity);
      if (exposed != null && exposed.contains(name)) {
        return doLexReadHelper(context, name);
      }
    }
    return context.currentScope().lookup(name).valueNumber();
  }

  /**
   * add an {@link AssignInstruction} to context.cfg() that copies rval to the
   * value number of local nm. Note that the caller is responsible for ensuring
   * that nm is defined in the local scope.
   */
  protected void doLocalWrite(WalkContext context, String nm, int rval) {
    if (!useLocalValuesForLexicalVars()) {
      CAstEntity entity = context.top();
      Set exposed = entity2ExposedNames.get(entity);
      if (exposed != null && exposed.contains(nm)) {
        // use a lexical write
        doLexicallyScopedWrite(context, nm, rval);
        return;
      }
    }
    int lval = context.currentScope().lookup(nm).valueNumber();
    if (lval != rval) {
      context.cfg().addInstruction(new AssignInstruction(context.cfg().currentInstruction, lval, rval));
    }
  }

  /**
   * Note that the caller is responsible for ensuring that name is defined in a
   * lexical scope.
   * 
   * @param node
   *          the AST node representing the read
   * @param context
   * @param name
   * @return
   */
  protected int doLexicallyScopedRead(CAstNode node, WalkContext context, final String name) {
    return doLexReadHelper(context, name);
  }

  /**
   * we only have this method to avoid having to pass a node parameter at other
   * call sites, as would be required for
   * {@link #doLexicallyScopedRead(CAstNode, WalkContext, String)}
   */
  private int doLexReadHelper(WalkContext context, final String name) {
    Symbol S = context.currentScope().lookup(name);
    Scope definingScope = S.getDefiningScope();
    CAstEntity E = definingScope.getEntity();
    // record in declaring scope that the name is exposed to a nested scope
//<<<<<<< .mine
//    Symbol S = context.currentScope().lookup(name);
//    CAstEntity E = S.getDefiningScope().getEntity();
//    addExposedName(E, E, name, S.getDefiningScope().lookup(name).valueNumber(), false, context);
//=======
    addExposedName(E, E, name, definingScope.lookup(name).valueNumber(), false, context);
//>>>>>>> .r4421

    final String entityName = context.getEntityName(E);
    if (useLocalValuesForLexicalVars()) {
      // lexically-scoped variables can be given a single vn in a method
//<<<<<<< .mine
//      Access A = new Access(name, context.getEntityName(E), vn);
//=======
//>>>>>>> .r4421

//<<<<<<< .mine
      // (context.top() is current entity)
      // record the name as exposed for the current entity, since if the name is
      // updated via a call to a nested function, SSA for the current entity may
      // need to be updated with the new definition
//      addExposedName(context.top(), E, name, vn, false, context);
//=======
      markExposedInEnclosingEntities(context, name, definingScope, E, entityName, false);
//>>>>>>> .r4421

//<<<<<<< .mine
      // record the access; later, the Accesses in the instruction
      // defining vn will be adjusted based on this information; see
      // patchLexicalAccesses()
//      addAccess(context, context.top(), A);
//=======
      return S.valueNumber();
//>>>>>>> .r4421

    } else {
      // lexically-scoped variables should be read from their scope each time
      int result = context.currentScope().allocateTempValue();
//<<<<<<< .mine
//      Access A = new Access(name, context.getEntityName(E), result);
//=======
      Access A = new Access(name, entityName, result);
      context.cfg().addInstruction(new AstLexicalRead(context.cfg().currentInstruction, A));
      markExposedInEnclosingEntities(context, name, definingScope, E, entityName, false);
      return result;
    }
  }

  /**
   * record name as exposed for the current entity and for all enclosing
   * entities up to that of the defining scope, since if the name is updated via
   * a call to a nested function, SSA for these entities may need to be updated
   * with the new definition
   * 
   * @param context
   * @param name
   * @param definingScope
   * @param E
   * @param entityName
   * @param isWrite
   */
  private void markExposedInEnclosingEntities(WalkContext context, final String name, Scope definingScope, CAstEntity E,
      final String entityName, boolean isWrite) {
    Scope curScope = context.currentScope();
    while (!curScope.equals(definingScope)) {
      final Symbol curSymbol = curScope.lookup(name);
      final int vn = curSymbol.valueNumber();
      final Access A = new Access(name, entityName, vn);
      final CAstEntity entity = curScope.getEntity();
      if (entity != definingScope.getEntity()) {
        addExposedName(entity, E, name, vn, isWrite, context);
        // record the access; later, the Accesses in the instruction
        // defining vn will be adjusted based on this information; see
        // patchLexicalAccesses()
        addAccess(context, entity, A);
      }
      curScope = curScope.getParent();
    }
  }

  /**
   * Note that the caller is responsible for ensuring that name is defined in a
   * lexical scope.
   * 
   */
  protected void doLexicallyScopedWrite(WalkContext context, String name, int rval) {
    Symbol S = context.currentScope().lookup(name);
    Scope definingScope = S.getDefiningScope();
    CAstEntity E = definingScope.getEntity();
    // record in declaring scope that the name is exposed to a nested scope
    addExposedName(E, E, name, definingScope.lookup(name).valueNumber(), true, context);

    if (useLocalValuesForLexicalVars()) {
      // lexically-scoped variables can be given a single vn in a method
      
      markExposedInEnclosingEntities(context, name, definingScope, E, context.getEntityName(E), true);

      context.cfg().addInstruction(new AssignInstruction(context.cfg().currentInstruction, S.valueNumber(), rval));
      // we add write instructions at every access for now
      // eventually, we may restructure the method to do a single combined write
      // before exit
      Access A = new Access(name, context.getEntityName(E), rval);
      context.cfg().addInstruction(new AstLexicalWrite(context.cfg().currentInstruction, A));

    } else {
      // lexically-scoped variables must be written in their scope each time
      Access A = new Access(name, context.getEntityName(E), rval);
      context.cfg().addInstruction(new AstLexicalWrite(context.cfg().currentInstruction, A));
      markExposedInEnclosingEntities(context, name, definingScope, E, context.getEntityName(E), true);
    }
  }

  /**
   * generate instructions for a read of a global
   */
  protected int doGlobalRead(CAstNode node, WalkContext context, String name) {
    Symbol S = context.currentScope().lookup(name);

    // Global variables can be treated as lexicals defined in the CG root, or
    if (treatGlobalsAsLexicallyScoped()) {

      // lexically-scoped variables can be given a single vn in a method, or
      if (useLocalValuesForLexicalVars()) {
        int vn = S.valueNumber();
        Access A = new Access(name, null, vn);

        addExposedName(context.top(), null, name, vn, false, context);
        addAccess(context, context.top(), A);

        return vn;

        // lexically-scoped variables can be read from their scope each time
      } else {
        int result = context.currentScope().allocateTempValue();
        Access A = new Access(name, null, result);
        context.cfg().addInstruction(new AstLexicalRead(context.cfg().currentInstruction, A));
        addAccess(context, context.top(), A);
        return result;
      }

      // globals can be treated as a single static location
    } else {
      int result = context.currentScope().allocateTempValue();
      FieldReference global = makeGlobalRef(name);
      context.cfg().addInstruction(new AstGlobalRead(context.cfg().currentInstruction, result, global));
      return result;
    }
  }

  /**
   * generate instructions for a write of a global
   */
  protected void doGlobalWrite(WalkContext context, String name, int rval) {
    Symbol S = context.currentScope().lookup(name);

    // Global variables can be treated as lexicals defined in the CG root, or
    if (treatGlobalsAsLexicallyScoped()) {

      // lexically-scoped variables can be given a single vn in a method, or
      if (useLocalValuesForLexicalVars()) {
        int vn = S.valueNumber();
        Access A = new Access(name, null, vn);

        addExposedName(context.top(), null, name, vn, true, context);
        addAccess(context, context.top(), A);

        context.cfg().addInstruction(new AssignInstruction(context.cfg().currentInstruction, vn, rval));
        context.cfg().addInstruction(new AstLexicalWrite(context.cfg().currentInstruction, A));

        // lexically-scoped variables can be read from their scope each time
      } else {
        Access A = new Access(name, null, rval);
        context.cfg().addInstruction(new AstLexicalWrite(context.cfg().currentInstruction, A));
    }
        addAccess(context, context.top(), A);
      }

      // globals can be treated as a single static location
    } else {
      FieldReference global = makeGlobalRef(name);
      context.cfg().addInstruction(new AstGlobalWrite(context.cfg().currentInstruction, global, rval));
    }
  }

  /**
   * generate instructions to check if ref has field, storing answer in result
   */
  protected void doIsFieldDefined(WalkContext context, int result, int ref, CAstNode field) {
    Assertions.UNREACHABLE();
  }

  /**
   * creates a reference to a global named globalName. the declaring type and
   * type of the global are both the root type.
   */
  protected FieldReference makeGlobalRef(String globalName) {
    TypeReference rootTypeRef = TypeReference.findOrCreate(loader.getReference(), AstTypeReference.rootTypeName);
    return FieldReference.findOrCreate(rootTypeRef, Atom.findOrCreateUnicodeAtom("global " + globalName), rootTypeRef);
  }

  protected final IClassLoader loader;

  /**
   * for handling languages that let you include other source files named
   * statically (e.g., ABAP)
   */
  protected final Map namedEntityResolver;

  protected final SSAInstructionFactory insts;

  protected AstTranslator(IClassLoader loader, Map namedEntityResolver, ArrayOpHandler arrayOpHandler) {
    this.loader = loader;
    this.namedEntityResolver = namedEntityResolver;
    this.arrayOpHandler = arrayOpHandler!=null? arrayOpHandler: this;
    this.insts = loader.getInstructionFactory();
  }

  protected AstTranslator(IClassLoader loader, Map namedEntityResolver) {
    this(loader, namedEntityResolver, null);
  }
  
  protected AstTranslator(IClassLoader loader) {
    this(loader, null);
  }

  /**
   * for keeping position information for the generated SSAInstructions and SSA
   * locals
   */
  private static class AstDebuggingInformation implements DebuggingInformation {
    private Position codeBodyPosition;

    private String[][] valueNumberNames;

    private Position[] instructionPositions;

    AstDebuggingInformation(Position codeBodyPosition, Position[] instructionPositions, String[] names) {
      this.codeBodyPosition = codeBodyPosition;

      this.instructionPositions = instructionPositions;

      valueNumberNames = new String[names.length][];
      for (int i = 0; i < names.length; i++) {
        if (names[i] != null) {
          valueNumberNames[i] = new String[] { names[i] };
        } else {
          valueNumberNames[i] = new String[0];
        }
      }
    }

    public Position getCodeBodyPosition() {
      return codeBodyPosition;
    }

    public Position getInstructionPosition(int instructionOffset) {
      return instructionPositions[instructionOffset];
    }

    public String[][] getSourceNamesForValues() {
      return valueNumberNames;
    }
  }

  public static final boolean DEBUG_ALL = false;

  public static final boolean DEBUG_TOP = DEBUG_ALL || false;

  public static final boolean DEBUG_CFG = DEBUG_ALL || false;

  public static final boolean DEBUG_NAMES = DEBUG_ALL || false;

  public static final boolean DEBUG_LEXICAL = DEBUG_ALL || false;

  /**
   * basic block implementation used in the CFGs constructed during the

   * IR-generating AST traversal
   */
  protected final static class PreBasicBlock implements INodeWithNumber, IBasicBlock {
    private static final int NORMAL = 0;

    private static final int HANDLER = 1;

    private static final int ENTRY = 2;

    private static final int EXIT = 3;

    private int kind = NORMAL;

    private int number = -1;

    private int firstIndex = -1;

    private int lastIndex = -2;

    private final List instructions = new ArrayList();

    public int getNumber() {
      return getGraphNodeId();
    }

    public int getGraphNodeId() {
      return number;
    }

    public void setGraphNodeId(int number) {
      this.number = number;
    }

    public int getFirstInstructionIndex() {
      return firstIndex;
    }

    void setFirstIndex(int firstIndex) {
      this.firstIndex = firstIndex;
    }

    public int getLastInstructionIndex() {
      return lastIndex;
    }

    void setLastIndex(int lastIndex) {
      this.lastIndex = lastIndex;
    }

    void makeExitBlock() {
      kind = EXIT;
    }

    void makeEntryBlock() {
      kind = ENTRY;
    }

    void makeHandlerBlock() {
      kind = HANDLER;
    }

    public boolean isEntryBlock() {
      return kind == ENTRY;
    }

    public boolean isExitBlock() {
      return kind == EXIT;
    }

    public boolean isHandlerBlock() {
      return kind == HANDLER;
    }

    public String toString() {
      return "PreBB" + number + ":" + firstIndex + ".." + lastIndex;
    }

    List instructions() {
      return instructions;
    }

    public boolean isCatchBlock() {
      return (lastIndex > -1) && (instructions.get(0) instanceof SSAGetCaughtExceptionInstruction);
    }

    public IMethod getMethod() {
      return null;
    }

    public Iterator iterator() {
      return instructions.iterator();
    }
  }

  protected final class UnwindState {
    final CAstNode unwindAst;

    final WalkContext astContext;

    final CAstVisitor astVisitor;

    UnwindState(CAstNode unwindAst, WalkContext astContext, CAstVisitor astVisitor) {
      this.unwindAst = unwindAst;
      this.astContext = astContext;
      this.astVisitor = astVisitor;
    }

    public UnwindState getParent() {
      return astContext.getUnwindState();
    }

    public int hashCode() {
      return astContext.hashCode() * unwindAst.hashCode() * astVisitor.hashCode();
    }

    public boolean equals(Object o) {
      if (o instanceof UnwindState) {
        if (((UnwindState) o).unwindAst != unwindAst)
          return false;
        if (((UnwindState) o).astVisitor != astVisitor)
          return false;
        if (getParent() == null) {
          return ((UnwindState) o).getParent() == null;
        } else {
          return getParent().equals(((UnwindState) o).getParent());
        }
      }

      return false;
    }

    boolean covers(UnwindState other) {
      if (equals(other))
        return true;
      if (getParent() != null)
        return getParent().covers(other);
      return false;
    }
  }

  /**
   * holds the control-flow graph as it is being constructed. When construction
   * is complete, information is stored in an {@link AstCFG}
   */
  public final class IncipientCFG extends SparseNumberedGraph {

    protected class Unwind {
      private final Map unwindData = new LinkedHashMap();

      /**
       * a cache of generated blocks
       */
      private final Map>, PreBasicBlock> code = new LinkedHashMap>, PreBasicBlock>();

      void setUnwindState(PreBasicBlock block, UnwindState context) {
        unwindData.put(block, context);
      }

      void setUnwindState(CAstNode node, UnwindState context) {
        unwindData.put(nodeToBlock.get(node), context);
      }

      /**
       * When adding an edge from source to target, it is possible that certain
       * exception-handling code needs to be executed before the control is
       * actually transfered to target. This method determines if this is the
       * case, and if so, it generates the exception handler blocks and adds an
       * appropriate edge to the target. It returns the basic block that should
       * be the target of the edge from source (target itself if there is no
       * exception-handling code, the initial catch block otherwise)
       */
      public PreBasicBlock findOrCreateCode(PreBasicBlock source, PreBasicBlock target, final boolean exception) {
        UnwindState sourceContext = unwindData.get(source);
        final CAstNode dummy = exception ? (new CAstImpl()).makeNode(CAstNode.EMPTY) : null;

        // no unwinding is needed, so jump to target block directly
        if (sourceContext == null)
          return target;

        WalkContext astContext = sourceContext.astContext;
        UnwindState targetContext = null;
        if (target != null)
          targetContext = unwindData.get(target);

        // in unwind context, but catch in same (or inner) unwind context
        if (targetContext != null && targetContext.covers(sourceContext))
          return target;

        Pair> key = Pair.make(sourceContext, Pair.make(target, exception));

        if (code.containsKey(key)) {
          return code.get(key);

        } else {
          int e = -1;
          PreBasicBlock currentBlock = getCurrentBlock();
          if (!isDeadBlock(currentBlock)) {
            addInstruction(insts.GotoInstruction(currentInstruction));
            newBlock(false);
          }
          PreBasicBlock startBlock = getCurrentBlock();
          if (exception) {
            setCurrentBlockAsHandler();
            e = sourceContext.astContext.currentScope().allocateTempValue();
            addInstruction(insts.GetCaughtExceptionInstruction(currentInstruction, startBlock.getNumber(), e));
            sourceContext.astContext.setCatchType(startBlock.getNumber(), defaultCatchType());
          }

    /**
          else
          while (sourceContext != null && (targetContext == null || !targetContext.covers(sourceContext))) {
            final CAstRewriter.Rewrite ast = (new CAstCloner(new CAstImpl()) {
              protected CAstNode flowOutTo(Map, CAstNode> nodeMap, CAstNode oldSource, Object label,
                  CAstNode oldTarget, CAstControlFlowMap orig, CAstSourcePositionMap src) {
                if (exception && !isExceptionLabel(label)) {
                  return dummy;
                } else {
                  return oldTarget;
                }
              }
            }).copy(sourceContext.unwindAst, sourceContext.astContext.getControlFlow(), sourceContext.astContext.getSourceMap(),
                sourceContext.astContext.top().getNodeTypeMap(), sourceContext.astContext.top().getAllScopedEntities());
            sourceContext.astVisitor.visit(ast.newRoot(), new DelegatingContext(sourceContext.astContext) {
              public CAstSourcePositionMap getSourceMap() {
                return ast.newPos();
              }

              public CAstControlFlowMap getControlFlow() {
                return ast.newCfg();
              }
            }, sourceContext.astVisitor);

            sourceContext = sourceContext.getParent();
          }

          PreBasicBlock endBlock = getCurrentBlock();
          if (exception) {
            addPreNode(dummy);
            doThrow(astContext, e);
          } else {
            addInstruction(insts.GotoInstruction(currentInstruction));
          }
          newBlock(false);

          addEdge(currentBlock, getCurrentBlock());
          if (target != null) {
            addEdge(endBlock, target);

            // `null' target is idiom for branch/throw to exit
          } else {
            addDelayedEdge(endBlock, exitMarker, exception);
          }

          code.put(key, startBlock);
          return startBlock;
        }
      }
    }

    private Unwind unwind = null;

    private final List blocks = new ArrayList();

    private final Map nodeToBlock = new LinkedHashMap();

    private final Map>> delayedEdges = new LinkedHashMap>>();

    private final Object exitMarker = new Object();

    private final Set deadBlocks = new LinkedHashSet();

    private final Set normalToExit = new LinkedHashSet();

    private final Set exceptionalToExit = new LinkedHashSet();

    private Position[] linePositions = new Position[10];

    private boolean hasCatchBlock = false;

    /**
     * does the method have any monitor operations?
     */
    private boolean hasMonitorOp = false;

    private int currentInstruction = 0;

    private PreBasicBlock currentBlock;

    public int getCurrentInstruction() {
      return currentInstruction;
    }

    public PreBasicBlock getCurrentBlock() {
      return currentBlock;
    }

    boolean hasCatchBlock() {
      return hasCatchBlock;
    }

    boolean hasMonitorOp() {
      return hasMonitorOp;
    }

    void noteCatchBlock() {
      hasCatchBlock = true;
    }

    Position[] getLinePositionMap() {
      return linePositions;
     * create a new basic block, and set it as the current block.
     * 
     * @param fallThruFromPrior
     *          should a fall-through edge be added from the previous block
     *          (value of currentBlock at entry)? if false, the newly created
     *          block is marked as a dead block, as it has no incoming edges.
     * @return the new block
     */
    public PreBasicBlock newBlock(boolean fallThruFromPrior) {
      // optimization: if we have a fall-through from an empty block, just
      // return the empty block
      if (fallThruFromPrior && !currentBlock.isEntryBlock() && currentBlock.instructions().size() == 0) {
        return currentBlock;
      }

      PreBasicBlock previous = currentBlock;
      currentBlock = new PreBasicBlock();
      addNode(currentBlock);
      blocks.add(currentBlock);

      if (DEBUG_CFG)
        System.err.println(("adding new block (node) " + currentBlock));
      if (fallThruFromPrior) {
        if (DEBUG_CFG)
          System.err.println(("adding fall-thru edge " + previous + " --> " + currentBlock));
        addEdge(previous, currentBlock);
      } else {
        deadBlocks.add(currentBlock);
      }

      return currentBlock;
    }

    /**
     * record a delayed edge addition from src to dst. Edge will be added when
     * appropriate; see {@link #checkForRealizedEdges(CAstNode)} and
     * {@link #checkForRealizedExitEdges(PreBasicBlock)}
     */
    private void addDelayedEdge(PreBasicBlock src, Object dst, boolean exception) {
      MapUtil.findOrCreateSet(delayedEdges, dst).add(Pair.make(src, exception));
    }

    void makeEntryBlock(PreBasicBlock bb) {
      bb.makeEntryBlock();
    }

    void makeExitBlock(PreBasicBlock bb) {
      bb.makeExitBlock();

      for (Iterator ps = getPredNodes(bb); ps.hasNext();)
        normalToExit.add(ps.next());

      // now that we have created the exit block, add the delayed edges to the
      // exit
      checkForRealizedExitEdges(bb);
    }

    void setCurrentBlockAsHandler() {
      currentBlock.makeHandlerBlock();
    }

    boolean hasDelayedEdges(CAstNode n) {
      return delayedEdges.containsKey(n);
    }

    /**
     * given some n which is now mapped by nodeToBlock, add any delayed edges to
     * n's block
     */
    private void checkForRealizedEdges(CAstNode n) {
      if (delayedEdges.containsKey(n)) {
        for (Iterator> ss = delayedEdges.get(n).iterator(); ss.hasNext();) {
          Pair s = ss.next();
          PreBasicBlock src = s.fst;
          boolean exception = s.snd;
          if (unwind == null) {
            addEdge(src, nodeToBlock.get(n));
          } else {
            PreBasicBlock target = nodeToBlock.get(n);
            addEdge(src, unwind.findOrCreateCode(src, target, exception));
          }
        }

        delayedEdges.remove(n);
      }
    }

    /**
     * add any delayed edges to the exit block
     */
    private void checkForRealizedExitEdges(PreBasicBlock exitBlock) {
      if (delayedEdges.containsKey(exitMarker)) {
        for (Iterator> ss = delayedEdges.get(exitMarker).iterator(); ss.hasNext();) {
          Pair s = ss.next();
          PreBasicBlock src = s.fst;
          boolean exception = s.snd;
          addEdge(src, exitBlock);
          if (exception)
            exceptionalToExit.add(src);
            normalToExit.add(src);
        }

        delayedEdges.remove(exitMarker);
      }
    }

    private void setUnwindState(CAstNode node, UnwindState context) {
      if (unwind == null)
        unwind = new Unwind();
      unwind.setUnwindState(node, context);
    }

    public void addPreNode(CAstNode n) {
      addPreNode(n, null);
    }

    /**
     * associate n with the current block, and update the current unwind state
     */
    public void addPreNode(CAstNode n, UnwindState context) {
      if (DEBUG_CFG)
        System.err.println(("adding pre-node " + n));
      nodeToBlock.put(n, currentBlock);
      deadBlocks.remove(currentBlock);
      if (context != null)
        setUnwindState(n, context);
      // now that we've associated n with a block, add associated delayed edges
      checkForRealizedEdges(n);
    }

    public void addPreEdge(CAstNode src, CAstNode dst, boolean exception) {
      assert nodeToBlock.containsKey(src);
      addPreEdge(nodeToBlock.get(src), dst, exception);
    }

    /**
     * if dst is associated with a basic block b, add an edge from src to b.
     * otherwise, record the edge addition as delayed.
     */
    public void addPreEdge(PreBasicBlock src, CAstNode dst, boolean exception) {
      if (dst == CAstControlFlowMap.EXCEPTION_TO_EXIT) {
        assert exception;
        addPreEdgeToExit(src, exception);
      } else if (nodeToBlock.containsKey(dst)) {
        PreBasicBlock target = nodeToBlock.get(dst);
        if (DEBUG_CFG)
          System.err.println(("adding pre-edge " + src + " --> " + dst));
        if (unwind == null) {
          addEdge(src, target);
        } else {
          addEdge(src, unwind.findOrCreateCode(src, target, exception));
        }
      } else {
        if (DEBUG_CFG)
          System.err.println(("adding delayed pre-edge " + src + " --> " + dst));
        addDelayedEdge(src, dst, exception);
      }
    }

    public void addPreEdgeToExit(CAstNode src, boolean exception) {
      assert nodeToBlock.containsKey(src);
      addPreEdgeToExit(nodeToBlock.get(src), exception);
    }

    public void addPreEdgeToExit(PreBasicBlock src, boolean exception) {
      if (unwind != null) {
        PreBasicBlock handlers = unwind.findOrCreateCode(src, null, exception);
        if (handlers != null) {
          addEdge(src, handlers);
          return;
        }
      }

      addDelayedEdge(src, exitMarker, exception);
    }

    public void addEdge(PreBasicBlock src, PreBasicBlock dst) {
      super.addEdge(src, dst);
      deadBlocks.remove(dst);
    }

    boolean isDeadBlock(PreBasicBlock block) {
      return deadBlocks.contains(block);
    }

    public PreBasicBlock getBlock(CAstNode n) {
      return nodeToBlock.get(n);
    }

    /**
     * mark the current position as the position for the instruction
     */
    private void noteLinePosition(int instruction) {
      if (linePositions.length < (instruction + 1)) {
        Position[] newData = new Position[instruction * 2 + 1];
        System.arraycopy(linePositions, 0, newData, 0, linePositions.length);
        linePositions = newData;
      }

      linePositions[instruction] = getCurrentPosition();
    }


    public void addInstruction(SSAInstruction n) {
      deadBlocks.remove(currentBlock);

      int inst = currentInstruction++;

      noteLinePosition(inst);

      if (currentBlock.instructions().size() == 0) {
        currentBlock.setFirstIndex(inst);
      } else {
        assert !(n instanceof SSAGetCaughtExceptionInstruction);
      }

      if (DEBUG_CFG) {
        System.err.println(("adding " + n + " at " + inst + " to " + currentBlock));
      }

      if (n instanceof SSAMonitorInstruction) {
        hasMonitorOp = true;
      }

      currentBlock.instructions().add(n);

      currentBlock.setLastIndex(inst);
    }
  }

  /**
   * data structure for the final CFG for a method, based on the information in
   * an {@link IncipientCFG}
   */
  protected final static class AstCFG extends AbstractCFG {
    private final SSAInstruction[] instructions;

    private final int[] instructionToBlockMap;

    private final String functionName;

    private final SymbolTable symtab;

    AstCFG(CAstEntity n, IncipientCFG icfg, SymbolTable symtab) {
      super(null);
      List blocks = icfg.blocks;

      this.symtab = symtab;
      functionName = n.getName();
      instructionToBlockMap = new int[blocks.size()];

      for (int i = 0; i < blocks.size(); i++)
        instructionToBlockMap[i] = blocks.get(i).getLastInstructionIndex();

      for (int i = 0; i < blocks.size(); i++) {
        PreBasicBlock block = blocks.get(i);
        this.addNode(block);
        if (block.isCatchBlock()) {
          setCatchBlock(i);
        }

        if (DEBUG_CFG)
          System.err.println(("added " + blocks.get(i) + " to final CFG as " + getNumber(blocks.get(i))));
      }
      if (DEBUG_CFG)
        System.err.println((getMaxNumber() + " blocks total"));

      init();

      for (int i = 0; i < blocks.size(); i++) {
        PreBasicBlock src = blocks.get(i);
        for (Iterator j = icfg.getSuccNodes(src); j.hasNext();) {
          PreBasicBlock dst = (PreBasicBlock) j.next();
          if (isCatchBlock(dst.getNumber()) || (dst.isExitBlock() && icfg.exceptionalToExit.contains(src))) {
            if (DEBUG_CFG)
              System.err.println(("exceptonal edge " + src + " -> " + dst));
            addExceptionalEdge(src, dst);
          }

          if (dst.isExitBlock() ? icfg.normalToExit.contains(src) : !isCatchBlock(dst.getNumber())) {
            if (DEBUG_CFG)
              System.err.println(("normal edge " + src + " -> " + dst));
            addNormalEdge(src, dst);
          }
        }
      }

      int x = 0;
      instructions = new SSAInstruction[icfg.currentInstruction];
      for (int i = 0; i < blocks.size(); i++) {
        List bi = blocks.get(i).instructions();
        for (int j = 0; j < bi.size(); j++) {
          instructions[x++] = bi.get(j);
        }
      }
    }

    public int hashCode() {
      return functionName.hashCode();
    }

    public boolean equals(Object o) {
      return (o instanceof AstCFG) && functionName.equals(((AstCFG) o).functionName);
    }

    public PreBasicBlock getBlockForInstruction(int index) {

      for (int i = 1; i < getNumberOfNodes() - 1; i++)
        if (index <= instructionToBlockMap[i])
          return getNode(i);

      return null;
    }

    public SSAInstruction[] getInstructions() {
      return instructions;
    }

    public int getProgramCounter(int index) {
      return index;
    }

    public String toString() {
      SSAInstruction[] insts = (SSAInstruction[]) getInstructions();
      StringBuffer s = new StringBuffer("CAst CFG of " + functionName);
      int params[] = symtab.getParameterValueNumbers();
      for (int i = 0; i < params.length; i++)
        s.append(" ").append(params[i]);
      s.append("\n");

      for (int i = 0; i < getNumberOfNodes(); i++) {
        PreBasicBlock bb = (PreBasicBlock) getNode(i);
        s.append(bb).append("\n");

        for (Iterator ss = getSuccNodes(bb); ss.hasNext();)
          s.append("    -->" + ss.next() + "\n");

        for (int j = bb.getFirstInstructionIndex(); j <= bb.getLastInstructionIndex(); j++)
          if (insts[j] != null)
            s.append("  " + insts[j].toString(symtab) + "\n");
      }

      s.append("-- END --");
      return s.toString();
    }
  }

  public static enum ScopeType {
    LOCAL, GLOBAL, SCRIPT, FUNCTION, TYPE
  };

  private static final boolean DEBUG = false;

  protected class FinalCAstSymbol implements CAstSymbol {
    private final String _name;

    private FinalCAstSymbol(String _name) {
      this._name = _name;
    }

    public String name() {
      return _name;
    }

    public boolean isFinal() {
      return true;
    }

    public boolean isCaseInsensitive() {
      return false;
    }

    public boolean isInternalName() {
      return false;
    }

    public Object defaultInitValue() {
      return null;
    }
  }

  public static class InternalCAstSymbol extends CAstSymbolImplBase {
    public InternalCAstSymbol(String _name) {
      super(_name, false, false, null);
    }

    public InternalCAstSymbol(String _name, boolean _isFinal) {
      super(_name, _isFinal, false, null);
    }

    public InternalCAstSymbol(String _name, boolean _isFinal, boolean _isCaseInsensitive) {
      super(_name, _isFinal, _isCaseInsensitive, null);
    }

    public InternalCAstSymbol(String _name, boolean _isFinal, boolean _isCaseInsensitive, Object _defaultInitValue) {
      super(_name, _isFinal, _isCaseInsensitive, _defaultInitValue);
    }

    public boolean isInternalName() {
      return true;
    }
  }

  /**
   * interface for name information stored in a symbol table.
   * 
   * @see Scope
   */
  protected interface Symbol {
    int valueNumber();

    Scope getDefiningScope();

    boolean isParameter();

    Object constant();

    void setConstant(Object s);

    boolean isFinal();
    boolean isInternalName();

    Object defaultInitValue();
  }

  /**
   * a scope in the symbol table built during AST traversal
   */
  public interface Scope {

    ScopeType type();

    int allocateTempValue();

    int getConstantValue(Object c);

    boolean isConstant(int valueNumber);

    Object getConstantObject(int valueNumber);

    void declare(CAstSymbol s);

    void declare(CAstSymbol s, int valueNumber);

    boolean isCaseInsensitive(String name);

    boolean contains(String name);

    Symbol lookup(String name);

    Iterator getAllNames();

    int size();

    boolean isGlobal(Symbol s);

    boolean isLexicallyScoped(Symbol s);

    CAstEntity getEntity();

    Scope getParent();
  }

  private static abstract class AbstractSymbol implements Symbol {
    private Object constantValue;

    private boolean isFinalValue;

    private final Scope definingScope;

    private Object defaultValue;

    AbstractSymbol(Scope definingScope, boolean isFinalValue, Object defaultValue) {
      this.definingScope = definingScope;
      this.isFinalValue = isFinalValue;
      this.defaultValue = defaultValue;
    }

    public boolean isFinal() {
      return isFinalValue;
    }

    public Object defaultInitValue() {
      return defaultValue;
    }

    public Object constant() {
      return constantValue;
    }

    public void setConstant(Object cv) {
      constantValue = cv;
    }

    public Scope getDefiningScope() {
      return definingScope;
    }
  };

  private abstract class AbstractScope implements Scope {
    private final Scope parent;

    private final Map values = new LinkedHashMap();

    private final Map caseInsensitiveNames = new LinkedHashMap();

    protected abstract SymbolTable getUnderlyingSymtab();

    public Scope getParent() {
      return parent;
    }

    public int size() {
      return getUnderlyingSymtab().getMaxValueNumber() + 1;
    }

    public Iterator getAllNames() {
      return values.keySet().iterator();
    }

            }
    public int allocateTempValue() {
      return getUnderlyingSymtab().newSymbol();
    }

    public int getConstantValue(Object o) {
      if (o instanceof Integer) {
        return getUnderlyingSymtab().getConstant(((Integer) o).intValue());
      } else if (o instanceof Float) {
        return getUnderlyingSymtab().getConstant(((Float) o).floatValue());
      } else if (o instanceof Double) {
        return getUnderlyingSymtab().getConstant(((Double) o).doubleValue());
      } else if (o instanceof Long) {
        }
        return getUnderlyingSymtab().getConstant(((Long) o).longValue());
      } else if (o instanceof String) {
        return getUnderlyingSymtab().getConstant((String) o);
      } else if (o instanceof Boolean) {
        return getUnderlyingSymtab().getConstant((Boolean) o);
      } else if (o instanceof Character) {
        return getUnderlyingSymtab().getConstant(((Character) o).charValue());
      } else if (o instanceof Byte) {
        return getUnderlyingSymtab().getConstant(((Byte) o).byteValue());
      } else if (o instanceof Short) {
        return getUnderlyingSymtab().getConstant(((Short) o).shortValue());
      } else if (o == null) {
        return getUnderlyingSymtab().getNullConstant();
      } else if (o == CAstControlFlowMap.SWITCH_DEFAULT) {
        return getUnderlyingSymtab().getConstant("__default label");
      } else {
        System.err.println(("cannot handle constant " + o));
        Assertions.UNREACHABLE();
        return -1;
      }
    }

    public boolean isConstant(int valueNumber) {
      return getUnderlyingSymtab().isConstant(valueNumber);
    }

    public Object getConstantObject(int valueNumber) {
      return getUnderlyingSymtab().getConstantValue(valueNumber);
    }

    public void declare(CAstSymbol s, int vn) {
      String nm = s.name();
      assert !contains(nm) : nm;
      if (s.isCaseInsensitive())
        caseInsensitiveNames.put(nm.toLowerCase(), nm);
      values.put(nm, makeSymbol(s, vn));
    }

    public void declare(CAstSymbol s) {
      String nm = s.name();
      if (!contains(nm) || lookup(nm).getDefiningScope() != this) {
        if (s.isCaseInsensitive())
          caseInsensitiveNames.put(nm.toLowerCase(), nm);
        values.put(nm, makeSymbol(s));
      } else {
        assert !s.isFinal() : "trying to redeclare " + nm;
      }
    }

    AbstractScope(Scope parent) {
      this.parent = parent;
    }

    private final String mapName(String nm) {
      String mappedName = caseInsensitiveNames.get(nm.toLowerCase());
      return (mappedName == null) ? nm : mappedName;
    }

    protected Symbol makeSymbol(CAstSymbol s) {
      return makeSymbol(s.name(), s.isFinal(), s.isInternalName(), s.defaultInitValue(), -1, this);
    }

    protected Symbol makeSymbol(CAstSymbol s, int vn) {
      return makeSymbol(s.name(), s.isFinal(), s.isInternalName(), s.defaultInitValue(), vn, this);
    }

    abstract protected Symbol makeSymbol(String nm, boolean isFinal, boolean isInternalName, Object defaultInitValue, int vn,
        Scope parent);

    public boolean isCaseInsensitive(String nm) {
      return caseInsensitiveNames.containsKey(nm.toLowerCase());
    }

    public Symbol lookup(String nm) {
      if (contains(nm)) {
        return values.get(mapName(nm));
      } else {
        Symbol scoped = parent.lookup(nm);
        if (scoped != null && getEntityScope() == this && (isGlobal(scoped) || isLexicallyScoped(scoped))) {
          values.put(nm,
              makeSymbol(nm, scoped.isFinal(), scoped.isInternalName(), scoped.defaultInitValue(), -1, scoped.getDefiningScope()));
          if (scoped.getDefiningScope().isCaseInsensitive(nm)) {
            caseInsensitiveNames.put(nm.toLowerCase(), nm);
          }
          return values.get(nm);
        } else {
          return scoped;
        }
      }
    }

    public boolean contains(String nm) {
      String mappedName = caseInsensitiveNames.get(nm.toLowerCase());
      return values.containsKey(mappedName == null ? nm : mappedName);
    }

    public boolean isGlobal(Symbol s) {
      return s.getDefiningScope().type() == ScopeType.GLOBAL;
    }

    public abstract boolean isLexicallyScoped(Symbol s);

    protected abstract AbstractScope getEntityScope();

    public abstract CAstEntity getEntity();
  };

  private AbstractScope makeScriptScope(final CAstEntity s, Scope parent) {
    return new AbstractScope(parent) {
      SymbolTable scriptGlobalSymtab = new SymbolTable(s.getArgumentCount());

      public SymbolTable getUnderlyingSymtab() {
        return scriptGlobalSymtab;
      }

      protected AbstractScope getEntityScope() {
        return this;
      }

      public boolean isLexicallyScoped(Symbol s) {
        if (isGlobal(s))
          return false;
        else
          return ((AbstractScope) s.getDefiningScope()).getEntity() != getEntity();
      }

      public CAstEntity getEntity() {
        return s;
      }

      public ScopeType type() {
        return ScopeType.SCRIPT;
      }

      protected Symbol makeSymbol(final String nm, final boolean isFinal, final boolean isInternalName,
          final Object defaultInitValue, int vn, Scope definer) {
        final int v = vn == -1 ? getUnderlyingSymtab().newSymbol() : vn;
        if (useDefaultInitValues() && defaultInitValue != null) {
          if (getUnderlyingSymtab().getValue(v) == null) {
            setDefaultValue(getUnderlyingSymtab(), v, defaultInitValue);
          }
        }
        return new AbstractSymbol(definer, isFinal, defaultInitValue) {
          public String toString() {
            return nm + ":" + System.identityHashCode(this);
          }

          public int valueNumber() {
            return v;
          }

          public boolean isInternalName() {
            return isInternalName;
          }

          public boolean isParameter() {
            return false;
          }
        };
      }
    };
  }

  protected int getArgumentCount(CAstEntity f) {
    return f.getArgumentCount();
  }
  
  protected String[] getArgumentNames(CAstEntity f) {
    return f.getArgumentNames();
  }
  
  private AbstractScope makeFunctionScope(final CAstEntity f, Scope parent) {
    return new AbstractScope(parent) {
      private final String[] params = getArgumentNames(f);

      private final SymbolTable functionSymtab = new SymbolTable(getArgumentCount(f));

      // ctor for scope object
      {
        for (int i = 0; i < getArgumentCount(f); i++) {
          final int yuck = i;
          declare(new CAstSymbol() {
            public String name() {
              return params[yuck];
            }

            public boolean isFinal() {
              return false;

            public boolean isCaseInsensitive() {
              return false;
            }

            public boolean isInternalName() {
              return false;
            }

            public Object defaultInitValue() {
              return null;
            }

          });
        }
      }
      public SymbolTable getUnderlyingSymtab() {
        return functionSymtab;
      }

      protected AbstractScope getEntityScope() {
        return this;
      }

      public boolean isLexicallyScoped(Symbol s) {
        if (isGlobal(s))
          return false;
        else
          return ((AbstractScope) s.getDefiningScope()).getEntity() != getEntity();
      }

      public CAstEntity getEntity() {
        return f;
      }

      public ScopeType type() {
        return ScopeType.FUNCTION;
      }

      private int find(String n) {
        for (int i = 0; i < params.length; i++) {
          if (n.equals(params[i])) {
            return i + 1;
          }
        }

        return -1;
      }

      protected Symbol makeSymbol(final String nm, final boolean isFinal, final boolean isInternalName,
          final Object defaultInitValue, final int valueNumber, Scope definer) {
        return new AbstractSymbol(definer, isFinal, defaultInitValue) {
          final int vn;

          {
            int x = find(nm);
            if (x != -1) {
              assert valueNumber == -1;
              vn = x;
            } else if (valueNumber != -1) {
              vn = valueNumber;
            } else {
              vn = getUnderlyingSymtab().newSymbol();
            }
            if (useDefaultInitValues() && defaultInitValue != null) {
              if (getUnderlyingSymtab().getValue(vn) == null) {
                setDefaultValue(getUnderlyingSymtab(), vn, defaultInitValue);
              }
            }
          }

          public String toString() {
            return nm + ":" + System.identityHashCode(this);
          }

          public int valueNumber() {
            return vn;
          }

          public boolean isInternalName() {
            return isInternalName;
          }

          public boolean isParameter() {
            return vn <= params.length;
          }
        };
      }
    };
  }

  private Scope makeLocalScope(CAstNode s, final Scope parent) {
    return new AbstractScope(parent) {
      public ScopeType type() {
        return ScopeType.LOCAL;
      }

      public SymbolTable getUnderlyingSymtab() {
        return ((AbstractScope) parent).getUnderlyingSymtab();
      }

      protected AbstractScope getEntityScope() {
        return ((AbstractScope) parent).getEntityScope();
      }

      public boolean isLexicallyScoped(Symbol s) {
        return ((AbstractScope) getEntityScope()).isLexicallyScoped(s);
      }

      public CAstEntity getEntity() {
        return ((AbstractScope) getEntityScope()).getEntity();
      }

      protected Symbol makeSymbol(final String nm, boolean isFinal, final boolean isInternalName, final Object defaultInitValue,
          int vn, Scope definer) {
        final int v = vn == -1 ? getUnderlyingSymtab().newSymbol() : vn;
        if (useDefaultInitValues() && defaultInitValue != null) {
          if (getUnderlyingSymtab().getValue(v) == null) {
            setDefaultValue(getUnderlyingSymtab(), v, defaultInitValue);
          }
        }
            throw new Error("cannot find " + name);
        return new AbstractSymbol(definer, isFinal, defaultInitValue) {
          public String toString() {
            return nm + ":" + System.identityHashCode(this);
          }

          public int valueNumber() {
            return v;
          }

          public boolean isInternalName() {
            return isInternalName;
          }

          public boolean isParameter() {
            return false;
          }
        };
      }
    };
  }

  private Scope makeGlobalScope() {
    final Map globalSymbols = new LinkedHashMap();
    final Map caseInsensitiveNames = new LinkedHashMap();
    return new Scope() {
      private final String mapName(String nm) {
        String mappedName = caseInsensitiveNames.get(nm.toLowerCase());
        return (mappedName == null) ? nm : mappedName;
      }

      public Scope getParent() {
        return null;
      }

      public boolean isGlobal(Symbol s) {
        return true;
      }

      public boolean isLexicallyScoped(Symbol s) {
        return false;
      }

      public CAstEntity getEntity() {
        return null;
      }

      public int size() {
        return globalSymbols.size();
      }

      public Iterator getAllNames() {
        return globalSymbols.keySet().iterator();
      }

      public int allocateTempValue() {
        throw new UnsupportedOperationException();
      }

      public int getConstantValue(Object c) {
        throw new UnsupportedOperationException();
      }

      public boolean isConstant(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public Object getConstantObject(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public ScopeType type() {
        return ScopeType.GLOBAL;
      }

      public boolean contains(String name) {
        return hasImplicitGlobals() || globalSymbols.containsKey(mapName(name));
      }

      public boolean isCaseInsensitive(String name) {
        return caseInsensitiveNames.containsKey(name.toLowerCase());
      }

      public Symbol lookup(final String name) {
        if (!globalSymbols.containsKey(mapName(name))) {
          if (hasImplicitGlobals()) {
            declare(new CAstSymbol() {
              public String name() {
                return name;
              }

              public boolean isFinal() {
                return false;
              }

              public boolean isCaseInsensitive() {
                return false;
              }

              public boolean isInternalName() {
                return false;
              }

              public Object defaultInitValue() {
                return null;
              }
            });
          } else if (hasSpecialUndeclaredVariables()) {
            return null;
          } else {
          }

        return globalSymbols.get(mapName(name));
      }

      public void declare(CAstSymbol s, int vn) {
        assert vn == -1;
        declare(s);
      }

      public void declare(final CAstSymbol s) {
        final String name = s.name();
        if (s.isCaseInsensitive()) {
          caseInsensitiveNames.put(name.toLowerCase(), name);
        }
        globalSymbols.put(name, new AbstractSymbol(this, s.isFinal(), s.defaultInitValue()) {
          public String toString() {
            return name + ":" + System.identityHashCode(this);
          }

          public boolean isParameter() {
            return false;
          }

          public boolean isInternalName() {
            return s.isInternalName();
          }

          public int valueNumber() {
            throw new UnsupportedOperationException();
          }
        });
      }
    };
  }

  protected Scope makeTypeScope(final CAstEntity type, final Scope parent) {
    final Map typeSymbols = new LinkedHashMap();
    final Map caseInsensitiveNames = new LinkedHashMap();
    return new Scope() {
      private final String mapName(String nm) {
        String mappedName = caseInsensitiveNames.get(nm.toLowerCase());
        return (mappedName == null) ? nm : mappedName;
      }

      public Scope getParent() {
        return parent;
      }

      public boolean isGlobal(Symbol s) {
        return false;
      }

      public boolean isLexicallyScoped(Symbol s) {
        return false;
      }

      public CAstEntity getEntity() {
        return type;
      }

      public int size() {
        return typeSymbols.size();
      }

      public Iterator getAllNames() {
        return typeSymbols.keySet().iterator();
      }

      public int allocateTempValue() {
        throw new UnsupportedOperationException();
      }

      public int getConstantValue(Object c) {
        throw new UnsupportedOperationException();
      }

      public boolean isConstant(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public Object getConstantObject(int valueNumber) {
        throw new UnsupportedOperationException();
      }

      public ScopeType type() {
        return ScopeType.TYPE;
      }

      public boolean contains(String name) {
        return typeSymbols.containsKey(mapName(name));
      }

      public boolean isCaseInsensitive(String name) {
        return caseInsensitiveNames.containsKey(name.toLowerCase());
      }

      public Symbol lookup(String nm) {
        if (typeSymbols.containsKey(mapName(nm)))
          return typeSymbols.get(mapName(nm));
        else {
          return parent.lookup(nm);
        }
      }

      public void declare(CAstSymbol s, int vn) {
        assert vn == -1;
        declare(s);
      }

      public void declare(final CAstSymbol s) {
        final String name = s.name();
        assert !s.isFinal();
        if (s.isCaseInsensitive())
          caseInsensitiveNames.put(name.toLowerCase(), name);
        typeSymbols.put(name, new AbstractSymbol(this, s.isFinal(), s.defaultInitValue()) {
          public String toString() {
            return name + ":" + System.identityHashCode(this);
          }

          public boolean isParameter() {
            return false;
          }

          public boolean isInternalName() {
            return s.isInternalName();
          }

          public int valueNumber() {
            throw new UnsupportedOperationException();
          }
        });
      }
    };
  }

  public interface WalkContext extends CAstVisitor.Context {

    ModuleEntry getModule();

    String getName();

    String file();

    CAstSourcePositionMap getSourceMap();

    CAstControlFlowMap getControlFlow();

    Scope currentScope();

    Set entityScopes();

    IncipientCFG cfg();

    UnwindState getUnwindState();

    void setCatchType(int blockNumber, TypeReference catchType);

    void setCatchType(CAstNode catchNode, TypeReference catchType);

    TypeReference[][] getCatchTypes();

    void addEntityName(CAstEntity e, String name);
    
    String getEntityName(CAstEntity e);

    boolean hasValue(CAstNode n);

    int setValue(CAstNode n, int v);

    int getValue(CAstNode n);
    
    Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet);
    
    Set getAccesses(CAstEntity e);
    
    Scope getGlobalScope();
    
  }

  private abstract class DelegatingContext implements WalkContext {
    private final WalkContext parent;

    DelegatingContext(WalkContext parent) {
      this.parent = parent;
    }

    public Set getAccesses(CAstEntity e) {
      return parent.getAccesses(e);
    }

    public ModuleEntry getModule() {
      return parent.getModule();
    }

    public String getName() {
      return parent.getName();
    }

    public String file() {
      return parent.file();
    }

    public CAstEntity top() {
      return parent.top();
    }

    public CAstSourcePositionMap getSourceMap() {
      return parent.getSourceMap();
    }

    public CAstControlFlowMap getControlFlow() {
      return parent.getControlFlow();
    }

    public Scope currentScope() {
      return parent.currentScope();
    }

    public Set entityScopes() {
      return parent.entityScopes();
    }

    public IncipientCFG cfg() {
      return parent.cfg();
    }

    public UnwindState getUnwindState() {
      return parent.getUnwindState();
    }

    public void setCatchType(int blockNumber, TypeReference catchType) {
      parent.setCatchType(blockNumber, catchType);
    }

    public void setCatchType(CAstNode catchNode, TypeReference catchType) {
      parent.setCatchType(catchNode, catchType);
    }

    public TypeReference[][] getCatchTypes() {
      return parent.getCatchTypes();
    }

    public void addEntityName(CAstEntity e, String name) {
      parent.addEntityName(e, name);
    }
    
    public String getEntityName(CAstEntity e) {
      return parent.getEntityName(e);
    }
    
    public boolean hasValue(CAstNode n) {
      return parent.hasValue(n);
    }

    public int setValue(CAstNode n, int v) {
      return parent.setValue(n, v);
    }

    public int getValue(CAstNode n) {
      return parent.getValue(n);
    }

    public Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet) {
      return parent.exposeNameSet(entity, writeSet);
    }

    public Scope getGlobalScope() {
      return parent.getGlobalScope();
    }
    
  }

  private class FileContext extends DelegatingContext {
    private final String fUnitName;

    public FileContext(WalkContext parent, String unitName) {
      super(parent);
      fUnitName = unitName;
    }

    public String getName() {
      return fUnitName;
    }
  }

  private class UnwindContext extends DelegatingContext {
    private final UnwindState state;

    UnwindContext(CAstNode unwindNode, WalkContext parent, CAstVisitor visitor) {
      super(parent);
      this.state = new UnwindState(unwindNode, parent, visitor);
    }

    public UnwindState getUnwindState() {
      return state;
    }
  }

  private abstract class EntityContext extends DelegatingContext {
    protected final CAstEntity topNode;

    protected final String name;

    EntityContext(WalkContext parent, CAstEntity s) {
      super(parent);
      this.topNode = s;
      this.name = composeEntityName(parent, s);
      addEntityName(s, this.name);
    }

    public String getName() {
      return name;
    }

    public CAstEntity top() {
      return topNode;
    }

    public CAstSourcePositionMap getSourceMap() {
      return top().getSourceMap();
    }

  }

  private class CodeEntityContext extends EntityContext {
    private final Scope topEntityScope;

    private final Set allEntityScopes;

    private final IncipientCFG cfg;

    private TypeReference[][] catchTypes = new TypeReference[0][];

    Set, Integer>> exposedReads;
    Set, Integer>> exposedWrites;
    
    Set accesses;
    
    /**
     * maps nodes in the current function to the value number holding their value
     * or, for constants, to their constant value.
     */
    private final Map results = new LinkedHashMap();

    CodeEntityContext(WalkContext parent, Scope entityScope, CAstEntity s) {
      super(parent, s);

      this.topEntityScope = entityScope;

      this.allEntityScopes = HashSetFactory.make();
      this.allEntityScopes.add(entityScope);

      cfg = new IncipientCFG();
    }

    public Set getAccesses(CAstEntity e) {
      if (e == topNode) {
        if (accesses == null) {
          accesses = HashSetFactory.make();
        }
        return accesses;
      } else {
        return super.getAccesses(e);
      }
    }
    
    @Override
    public Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet) {
      if (entity == topNode) {
       if (writeSet) {
         if (exposedWrites == null) {
           exposedWrites = HashSetFactory.make();
         }
         return exposedWrites;
       } else {
         if (exposedReads == null) {
           exposedReads = HashSetFactory.make();
         }
         return exposedReads;         
       }
      } else {
        return super.exposeNameSet(entity, writeSet);
      }
    }


    public CAstControlFlowMap getControlFlow() {
      return top().getControlFlow();
    }

    public IncipientCFG cfg() {
      return cfg;
    }

    public Scope currentScope() {
      return topEntityScope;
    }

    public Set entityScopes() {
      return allEntityScopes;
    }

    public UnwindState getUnwindState() {
      return null;
    }

    public void setCatchType(CAstNode catchNode, TypeReference catchType) {
      setCatchType(cfg.getBlock(catchNode).getNumber(), catchType);
    }

    public void setCatchType(int blockNumber, TypeReference catchType) {
      if (catchTypes.length <= blockNumber) {
        TypeReference[][] data = new TypeReference[blockNumber + 1][];
        System.arraycopy(catchTypes, 0, data, 0, catchTypes.length);
        catchTypes = data;
      }

      if (catchTypes[blockNumber] == null) {
        catchTypes[blockNumber] = new TypeReference[] { catchType };
      } else {
        TypeReference[] data = catchTypes[blockNumber];

        for (int i = 0; i < data.length; i++) {
          if (data[i] == catchType) {
            return;
          }
        }

        TypeReference[] newData = new TypeReference[data.length + 1];
        System.arraycopy(data, 0, newData, 0, data.length);
        newData[data.length] = catchType;

        catchTypes[blockNumber] = newData;
      }
    }

    public TypeReference[][] getCatchTypes() {
      return catchTypes;
    }
    
    public boolean hasValue(CAstNode n) {
      return results.containsKey(n);
    }

    public final int setValue(CAstNode n, int v) {
      results.put(n, new Integer(v));
      return v;
    }

    public final int getValue(CAstNode n) {
      if (results.containsKey(n))
        return results.get(n).intValue();
      else {
        if (DEBUG) {
          System.err.println(("no value for " + n.getKind()));
        }
        return -1;
      }
    }

  }

  private final class TypeContext extends EntityContext {

    private TypeContext(WalkContext parent, CAstEntity n) {
      super(parent, n);
    }

    public CAstControlFlowMap getControlFlow() {
      Assertions.UNREACHABLE("TypeContext.getControlFlow()");
      return null;
    }

    public IncipientCFG cfg() {
      Assertions.UNREACHABLE("TypeContext.cfg()");
      return null;
    }

    public UnwindState getUnwindState() {
      Assertions.UNREACHABLE("TypeContext.getUnwindState()");
      return null;
    }
  }

  private class LocalContext extends DelegatingContext {
    private final Scope localScope;

    LocalContext(WalkContext parent, Scope localScope) {
      super(parent);
      this.localScope = localScope;
      parent.entityScopes().add(localScope);
    }

    public Scope currentScope() {
      return localScope;
    }
  }

  /**
   * lexical access information for some entity scope. used during call graph
   * construction to handle lexical accesses.
   */
  public static class AstLexicalInformation implements LexicalInformation {
    /**
     * the name of this function, as it appears in the definer portion of a
     * lexical name
     */
    private final String functionLexicalName;

    /**
     * names possibly accessed in a nested lexical scope, represented as pairs
     * (name,nameOfDefiningEntity)
     */
    private final Pair[] exposedNames;

    /**
     * map from instruction index and exposed name (via its index in
     * {@link #exposedNames}) to the value number for the name at that
     * instruction index. This can vary at different instructions due to SSA
     * (and this information is updated during {@link SSAConversion}).
     */
    private final int[][] instructionLexicalUses;

    /**
     * maps each exposed name (via its index in {@link #exposedNames}) to its
     * value number at method exit.
     */
    private final int[] exitLexicalUses;

    /**
     * the names of the enclosing methods declaring names that are lexically
     * accessed by the entity
     */
    private final String[] scopingParents;

    /**
     * all value numbers appearing as entries in {@link #instructionLexicalUses}
     * and {@link #exitLexicalUses}, computed lazily
     */
    private MutableIntSet allExposedUses = null;

    /**
     * names of exposed variables of this method that cannot be written outside
     */
    private final Set readOnlyNames;

    @SuppressWarnings("unchecked")
    public AstLexicalInformation(AstLexicalInformation original) {
      this.functionLexicalName = original.functionLexicalName;

      if (original.exposedNames != null) {
        exposedNames = new Pair[original.exposedNames.length];
        for (int i = 0; i < exposedNames.length; i++) {
          exposedNames[i] = Pair.make(original.exposedNames[i].fst, original.exposedNames[i].snd);
        }
      } else {
        exposedNames = null;
      }

      instructionLexicalUses = new int[original.instructionLexicalUses.length][];
      for (int i = 0; i < instructionLexicalUses.length; i++) {
        int[] x = original.instructionLexicalUses[i];
        if (x != null) {
          instructionLexicalUses[i] = new int[x.length];
          for (int j = 0; j < x.length; j++) {
            instructionLexicalUses[i][j] = x[j];
          }
        }
      }

      if (original.exitLexicalUses != null) {
        exitLexicalUses = new int[original.exitLexicalUses.length];
        for (int i = 0; i < exitLexicalUses.length; i++) {
          exitLexicalUses[i] = original.exitLexicalUses[i];
        }
      } else {
        exitLexicalUses = null;
      }

      if (original.scopingParents != null) {
        scopingParents = new String[original.scopingParents.length];
        for (int i = 0; i < scopingParents.length; i++) {
          scopingParents[i] = original.scopingParents[i];
        }
      } else {
        scopingParents = null;
      }

      readOnlyNames = original.readOnlyNames;
    }

    private int[] buildLexicalUseArray(Pair, Integer>[] exposedNames, String entityName) {
      if (exposedNames != null) {
        int[] lexicalUses = new int[exposedNames.length];
        for (int j = 0; j < exposedNames.length; j++) {
          if (entityName == null || entityName.equals(exposedNames[j].fst.snd)) {
            lexicalUses[j] = exposedNames[j].snd;
          } else {
            lexicalUses[j] = -1;
          }
        }

        return lexicalUses;
      } else {
        return null;
      }
    }

    private Pair[] buildLexicalNamesArray(Pair, Integer>[] exposedNames) {
      if (exposedNames != null) {
        @SuppressWarnings("unchecked")
        Pair[] lexicalNames = new Pair[exposedNames.length];
        for (int j = 0; j < exposedNames.length; j++) {
          lexicalNames[j] = exposedNames[j].fst;
        }

        return lexicalNames;
      } else {
        return null;
      }
    }

    @SuppressWarnings("unchecked")
    AstLexicalInformation(String entityName, Scope scope, SSAInstruction[] instrs,
        Set, Integer>> exposedNamesForReadSet,
        Set, Integer>> exposedNamesForWriteSet, Set accesses) {
      this.functionLexicalName = entityName;

      Pair, Integer>[] EN = null;
      if (exposedNamesForReadSet != null || exposedNamesForWriteSet != null) {
        Set, Integer>> exposedNamesSet = new HashSet, Integer>>();
        if (exposedNamesForReadSet != null) {
          exposedNamesSet.addAll(exposedNamesForReadSet);
        }
        if (exposedNamesForWriteSet != null) {
          exposedNamesSet.addAll(exposedNamesForWriteSet);
        }
        EN = exposedNamesSet.toArray(new Pair[exposedNamesSet.size()]);
      }

      if (exposedNamesForReadSet != null) {
        Set readOnlyNames = new HashSet();
        for (Pair, Integer> v : exposedNamesForReadSet) {
          if (entityName != null && entityName.equals(v.fst.snd)) {
            readOnlyNames.add(v.fst.fst);
          }
        }
        if (exposedNamesForWriteSet != null) {
          for (Pair, Integer> v : exposedNamesForWriteSet) {
            if (entityName != null && entityName.equals(v.fst.snd)) {
              readOnlyNames.remove(v.fst.fst);
            }
          }
        }
        this.readOnlyNames = readOnlyNames;
      } else {
        this.readOnlyNames = null;
      }

      this.exposedNames = buildLexicalNamesArray(EN);

      // the value numbers stored in exitLexicalUses and instructionLexicalUses
      // are identical at first; they will be updated
      // as needed during the final SSA conversion
      this.exitLexicalUses = buildLexicalUseArray(EN, entityName);

      this.instructionLexicalUses = new int[instrs.length][];
      for (int i = 0; i < instrs.length; i++) {
        if (instrs[i] instanceof SSAAbstractInvokeInstruction) {
          this.instructionLexicalUses[i] = buildLexicalUseArray(EN, null);
        }
      }

      if (accesses != null) {
        Set parents = new LinkedHashSet();
        for (Iterator ACS = accesses.iterator(); ACS.hasNext();) {
          Access AC = ACS.next();
          if (AC.variableDefiner != null) {
            parents.add(AC.variableDefiner);
          }
        }
        scopingParents = parents.toArray(new String[parents.size()]);

        if (DEBUG_LEXICAL) {
          System.err.println(("scoping parents of " + scope.getEntity()));
          System.err.println(parents.toString());
        }

      } else {
        scopingParents = null;
      }

      if (DEBUG_NAMES) {
        System.err.println(("lexical uses of " + scope.getEntity()));
        for (int i = 0; i < instructionLexicalUses.length; i++) {
          if (instructionLexicalUses[i] != null) {
            System.err.println(("  lexical uses of " + instrs[i]));
            for (int j = 0; j < instructionLexicalUses[i].length; j++) {
              System.err.println(("    " + this.exposedNames[j].fst + ": " + instructionLexicalUses[i][j]));
            }
          }
        }
      }
    }

    public int[] getExitExposedUses() {
      return exitLexicalUses;
    }

    private static final int[] NONE = new int[0];

    public int[] getExposedUses(int instructionOffset) {
      return instructionLexicalUses[instructionOffset] == null ? NONE : instructionLexicalUses[instructionOffset];
    }

    public IntSet getAllExposedUses() {
      if (allExposedUses == null) {
        allExposedUses = IntSetUtil.make();
        if (exitLexicalUses != null) {
          for (int i = 0; i < exitLexicalUses.length; i++) {
            if (exitLexicalUses[i] > 0) {
              allExposedUses.add(exitLexicalUses[i]);
            }
          }
        }
        if (instructionLexicalUses != null) {
          for (int i = 0; i < instructionLexicalUses.length; i++) {
            if (instructionLexicalUses[i] != null) {
              for (int j = 0; j < instructionLexicalUses[i].length; j++) {
                if (instructionLexicalUses[i][j] > 0) {
                  allExposedUses.add(instructionLexicalUses[i][j]);
                }
              }
            }
          }
        }
      }

      return allExposedUses;
    }

    public Pair[] getExposedNames() {
      return exposedNames;
    }

    public String[] getScopingParents() {
      return scopingParents;
    }

    /**

     * reset cached info about value numbers that may have changed
     */
    public void handleAlteration() {
      allExposedUses = null;
    }

    public boolean isReadOnly(String name) {
      return readOnlyNames != null && readOnlyNames.contains(name);
    }

    public String getScopingName() {
      return functionLexicalName;
    }
  };
 
  /**
   * record that in entity e, the access is performed.
   * 
   * If {@link #useLocalValuesForLexicalVars()} is true, the access is performed
   * using a local variable. in
   * {@link #patchLexicalAccesses(SSAInstruction[], Set)}, this information is
   * used to update an instruction that performs all the accesses at the
   * beginning of the method and defines the locals.
   */
  private void addAccess(WalkContext context, CAstEntity e, Access access) {
    context.getAccesses(e).add(access);
  }

  /**
   * Record that a name assigned a value number in the scope of entity may be
   * accessed by a lexically nested scope, i.e., the name may be
   * exposed to lexically nested scopes. This information is needed
   * during call graph construction to properly model the data flow due to the
   * access in the nested scope.
   * 
   * @param entity
   *          an entity in whose scope name is assigned a value number
   * @param declaration
   *          the declaring entity for name (possibly an enclosing scope of
   *          entity, in the case where entity
   *          {@link #useLocalValuesForLexicalVars() accesses the name via a
   *          local})
   * @param name
   *          the accessed name
   * @param valueNumber
   *          the name's value number in the scope of entity
   */
  private void addExposedName(CAstEntity entity, CAstEntity declaration, String name, int valueNumber, boolean isWrite, WalkContext context) {
    Pair, Integer> newVal = Pair.make(Pair.make(name, context.getEntityName(declaration)), valueNumber);
    context.exposeNameSet(entity, isWrite).add(newVal);
  }

  private void setDefaultValue(SymbolTable symtab, int vn, Object value) {
    if (value == CAstSymbol.NULL_DEFAULT_VALUE) {
      symtab.setDefaultValue(vn, null);
    } else {
      symtab.setDefaultValue(vn, value);
    }
  }

  protected IUnaryOpInstruction.IOperator translateUnaryOpcode(CAstNode op) {
    if (op == CAstOperator.OP_BITNOT)
      return AstConstants.UnaryOp.BITNOT;
    else if (op == CAstOperator.OP_NOT)
      return IUnaryOpInstruction.Operator.NEG;
    else if (op == CAstOperator.OP_SUB)
      return AstConstants.UnaryOp.MINUS;
    else if (op == CAstOperator.OP_ADD)
      return AstConstants.UnaryOp.PLUS;
    else
      Assertions.UNREACHABLE("cannot translate " + CAstPrinter.print(op));
    return null;

  }

  protected IBinaryOpInstruction.IOperator translateBinaryOpcode(CAstNode op) {
    if (op == CAstOperator.OP_ADD)
      return BinaryOpInstruction.Operator.ADD;
    else if (op == CAstOperator.OP_DIV)
      return BinaryOpInstruction.Operator.DIV;
    else if (op == CAstOperator.OP_LSH)
      return ShiftInstruction.Operator.SHL;
    else if (op == CAstOperator.OP_MOD)
      return BinaryOpInstruction.Operator.REM;
    else if (op == CAstOperator.OP_MUL)
      return BinaryOpInstruction.Operator.MUL;
    else if (op == CAstOperator.OP_RSH)
      return ShiftInstruction.Operator.SHR;
    else if (op == CAstOperator.OP_SUB)
      return BinaryOpInstruction.Operator.SUB;
    else if (op == CAstOperator.OP_URSH)
      return ShiftInstruction.Operator.USHR;
    else if (op == CAstOperator.OP_BIT_AND)
      return BinaryOpInstruction.Operator.AND;
    else if (op == CAstOperator.OP_BIT_OR)
      return BinaryOpInstruction.Operator.OR;
    else if (op == CAstOperator.OP_BIT_XOR)
      return BinaryOpInstruction.Operator.XOR;
    else if (op == CAstOperator.OP_CONCAT)
      return AstConstants.BinaryOp.CONCAT;
    else if (op == CAstOperator.OP_EQ)
      return AstConstants.BinaryOp.EQ;
    else if (op == CAstOperator.OP_STRICT_EQ)
      return AstConstants.BinaryOp.STRICT_EQ;
    else if (op == CAstOperator.OP_GE)
      return AstConstants.BinaryOp.GE;
    else if (op == CAstOperator.OP_GT)
      return AstConstants.BinaryOp.GT;
    else if (op == CAstOperator.OP_LE)
      return AstConstants.BinaryOp.LE;
    else if (op == CAstOperator.OP_LT)
      return AstConstants.BinaryOp.LT;
    else if (op == CAstOperator.OP_NE)
      return AstConstants.BinaryOp.NE;
    else if (op == CAstOperator.OP_STRICT_NE)
      return AstConstants.BinaryOp.STRICT_NE;
    else {
      Assertions.UNREACHABLE("cannot translate " + CAstPrinter.print(op));
      return null;
    }
  }

  protected IConditionalBranchInstruction.IOperator translateConditionOpcode(CAstNode op) {
    if (op == CAstOperator.OP_EQ)
      return ConditionalBranchInstruction.Operator.EQ;
    else if (op == CAstOperator.OP_GE)
      return ConditionalBranchInstruction.Operator.GE;
    else if (op == CAstOperator.OP_GT)
      return ConditionalBranchInstruction.Operator.GT;
    else if (op == CAstOperator.OP_LE)
      return ConditionalBranchInstruction.Operator.LE;
    else if (op == CAstOperator.OP_LT)
      return ConditionalBranchInstruction.Operator.LT;
    else if (op == CAstOperator.OP_NE)
      return ConditionalBranchInstruction.Operator.NE;

    else {
      Assertions.UNREACHABLE("cannot translate " + CAstPrinter.print(op));
      return null;
    }
  }

  private String[] makeNameMap(CAstEntity n, Set scopes) {
    // all scopes share the same underlying symtab, which is what
    // size really refers to.
    String[] map = new String[scopes.iterator().next().size() + 1];

    if (DEBUG_NAMES) {
      System.err.println(("names array of size " + map.length));
    }

    for (Iterator S = scopes.iterator(); S.hasNext();) {
      Scope scope = S.next();
      for (Iterator I = scope.getAllNames(); I.hasNext();) {
        String nm = I.next();
        Symbol v = (Symbol) scope.lookup(nm);

        if (v.isInternalName()) {
          continue;
        }

        // constants can flow to multiple variables
        if (scope.isConstant(v.valueNumber()))
          continue;

        assert map[v.valueNumber()] == null || map[v.valueNumber()].equals(nm) : "value number " + v.valueNumber()
            + " mapped to multiple names in " + n.getName() + ": " + nm + " and " + map[v.valueNumber()];

        map[v.valueNumber()] = nm;

        if (DEBUG_NAMES) {
          System.err.println(("mapping name " + nm + " to " + v.valueNumber()));
        }
      }
    }

    return map;
  }

    visitor.visit(n.getChild(1), context, visitor);
  protected final CAstType getTypeForNode(WalkContext context, CAstNode node) {
    if (context.top().getNodeTypeMap() != null) {
      return context.top().getNodeTypeMap().getNodeType(node);
    } else {
      return null;
    }
  }

  /**
   * find any AstLexicalAccess instructions in instrs with a zero access count,
   * and change them to perform specified accesses. If accesses is empty, null
   * out the pointers to the AstLexicalAccess instructions in the array.
   * 
    return false;
   * Presumably, such empty AstLexicalAccess instructions should only exist if
   * {@link #useLocalValuesForLexicalVars()} returns true?
   */
  private void patchLexicalAccesses(SSAInstruction[] instrs, Set accesses) {
    Access[] AC = accesses == null || accesses.isEmpty() ? (Access[]) null : (Access[]) accesses.toArray(new Access[accesses.size()]);
    for (int i = 0; i < instrs.length; i++) {
      if (instrs[i] instanceof AstLexicalAccess && ((AstLexicalAccess) instrs[i]).getAccessCount() == 0) {
        // should just be AstLexicalRead for now; may add support for
        // AstLexicalWrite later
        assert instrs[i] instanceof AstLexicalRead;
        assert useLocalValuesForLexicalVars();
        if (AC != null) {
          ((AstLexicalAccess) instrs[i]).setAccesses(AC);
        } else {
          instrs[i] = null;
        }
      }
    }
  }

  private Position getPosition(CAstSourcePositionMap map, CAstNode n) {
    if (map.getPosition(n) != null) {
      return map.getPosition(n);
    } else {
      for (int i = 0; i < n.getChildCount(); i++) {
        Position p = getPosition(map, n.getChild(i));
        if (p != null) {
          return p;
        }
      }

      return null;
    }
  }

  protected WalkContext makeFileContext(WalkContext c, CAstEntity n) {
    return new FileContext((WalkContext) c, n.getName());
  }

  protected WalkContext makeTypeContext(WalkContext c, CAstEntity n) {
    return new TypeContext((WalkContext) c, n);
  }

  protected WalkContext makeCodeContext(WalkContext c, CAstEntity n) {
    WalkContext context = (WalkContext) c;
    AbstractScope scope;
    if (n.getKind() == CAstEntity.SCRIPT_ENTITY)
      scope = makeScriptScope(n, context.currentScope());
    else
      scope = makeFunctionScope(n, context.currentScope());
    return new CodeEntityContext(context, scope, n);
  }

  protected boolean enterEntity(final CAstEntity n, WalkContext context, CAstVisitor visitor) {
    if (DEBUG_TOP)
      System.err.println(("translating " + n.getName()));
    return false;
  }

  protected boolean visitFileEntity(CAstEntity n, WalkContext context, WalkContext fileContext, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFileEntity(CAstEntity n, WalkContext context, WalkContext fileContext, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitFieldEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFieldEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) {
    // Define a new field in the enclosing type, if the language we're
    // processing allows such.
    CAstEntity topEntity = context.top(); // better be a type
    assert topEntity.getKind() == CAstEntity.TYPE_ENTITY : "Parent of field entity is not a type???";
    defineField(topEntity, (WalkContext) context, n);
  }

  protected boolean visitGlobalEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveGlobalEntity(CAstEntity n, WalkContext context, CAstVisitor visitor) {
    // Define a new field in the enclosing type, if the language we're
    // processing allows such.
    context.getGlobalScope().declare(new CAstSymbolImpl(n.getName()));
  }

  protected boolean visitTypeEntity(CAstEntity n, WalkContext context, WalkContext typeContext, CAstVisitor visitor) {
    return !defineType(n, (WalkContext) context);
  }

    CAstEntity fn = (CAstEntity) n.getChild(0).getValue();
  protected void leaveTypeEntity(CAstEntity n, WalkContext context, WalkContext typeContext, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitFunctionEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    if (n.getAST() == null) // presumably abstract
      declareFunction(n, (WalkContext) context);
    else
      initFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
    return false;
  }

  protected void leaveFunctionEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    if (n.getAST() != null) // non-abstract
      closeFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
  }

  protected boolean visitMacroEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    return true;
  }

  protected boolean visitScriptEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    declareFunction(n, (WalkContext) codeContext);
    initFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
    return false;
  }

  protected void leaveScriptEntity(CAstEntity n, WalkContext context, WalkContext codeContext, CAstVisitor visitor) {
    closeFunctionEntity(n, (WalkContext) context, (WalkContext) codeContext);
  }

  public void initFunctionEntity(final CAstEntity n, WalkContext parentContext, WalkContext functionContext) {
    // entry block
    functionContext.cfg().makeEntryBlock(functionContext.cfg().newBlock(false));
    // first real block
    functionContext.cfg().newBlock(true);
    // prologue code, if any
    doPrologue(functionContext);
  }

  public void closeFunctionEntity(final CAstEntity n, WalkContext parentContext, WalkContext functionContext) {
    // exit block
    functionContext.cfg().makeExitBlock(functionContext.cfg().newBlock(true));

    // create code entry stuff for this entity
    SymbolTable symtab = ((AbstractScope) functionContext.currentScope()).getUnderlyingSymtab();
    TypeReference[][] catchTypes = functionContext.getCatchTypes();
    AstCFG cfg = new AstCFG(n, functionContext.cfg(), symtab);
    Position[] line = functionContext.cfg().getLinePositionMap();
    boolean katch = functionContext.cfg().hasCatchBlock();
    boolean monitor = functionContext.cfg().hasMonitorOp();
    String[] nms = makeNameMap(n, functionContext.entityScopes());

    /*
     * Set reachableBlocks = DFS.getReachableNodes(cfg,
     * Collections.singleton(cfg.entry()));
     * Assertions._assert(reachableBlocks.size() == cfg.getNumberOfNodes(),
     * cfg.toString());
     */

    // (put here to allow subclasses to handle stuff in scoped entities)
    // assemble lexical information
    patchLexicalAccesses(cfg.getInstructions(), functionContext.getAccesses(n));
    AstLexicalInformation LI = new AstLexicalInformation(functionContext.getEntityName(n), (AbstractScope) functionContext.currentScope(), cfg.getInstructions(),
        functionContext.exposeNameSet(n, false), 
        functionContext.exposeNameSet(n, true), 
        functionContext.getAccesses(n));

    DebuggingInformation DBG = new AstDebuggingInformation(n.getPosition(), line, nms);

    // actually make code body
    defineFunction(n, parentContext, cfg, symtab, katch, catchTypes, monitor, LI, DBG);
  }

  protected WalkContext makeLocalContext(WalkContext context, CAstNode n) {
    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
    return new LocalContext((WalkContext) context, makeLocalScope(n, ((WalkContext) context).currentScope()));
  }

  protected WalkContext makeUnwindContext(WalkContext context, CAstNode n, CAstVisitor visitor) {
    // here, n represents the "finally" block of the unwind
    return new UnwindContext(n, (WalkContext) context, visitor);
  }

  private Map> entity2ExposedNames;
  protected int processFunctionExpr(CAstNode n, WalkContext context) {
    declareFunction(fn, context);
    int result = context.currentScope().allocateTempValue();
    int ex = context.currentScope().allocateTempValue();
    doMaterializeFunction(n, context, result, ex, fn);
    return result;
  }

  protected boolean visitFunctionExpr(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFunctionExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    int result = processFunctionExpr(n, c);
    c.setValue(n, result);
  }

  protected boolean visitFunctionStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveFunctionStmt(CAstNode n, WalkContext context, CAstVisitor visitor) {
    int result = processFunctionExpr(n, context);
    CAstEntity fn = (CAstEntity) n.getChild(0).getValue();
    // FIXME: handle redefinitions of functions
    Scope cs = context.currentScope();
    if (cs.contains(fn.getName()) && !cs.isLexicallyScoped(cs.lookup(fn.getName())) && !cs.isGlobal(cs.lookup(fn.getName()))) {
      // if we already have a local with the function's name, write the function
      // value to that local
      assignValue(n, context, cs.lookup(fn.getName()), fn.getName(), result);
    } else if (topLevelFunctionsInGlobalScope() && context.top().getKind() == CAstEntity.SCRIPT_ENTITY) {
      context.getGlobalScope().declare(new FinalCAstSymbol(fn.getName()));
      assignValue(n, context, cs.lookup(fn.getName()), fn.getName(), result);
    } else {
      context.currentScope().declare(new FinalCAstSymbol(fn.getName()), result);
    }
  }

  protected boolean visitLocalScope(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveLocalScope(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(0)));
  }

  protected boolean visitBlockExpr(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(n.getChildCount() - 1)));
  }

  protected boolean visitBlockStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitLoop(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    // loop test block
    context.cfg().newBlock(true);
    PreBasicBlock headerB = context.cfg().getCurrentBlock();
    visitor.visit(n.getChild(0), context, visitor);

    assert c.getValue(n.getChild(0)) != -1 : "error in loop test " + CAstPrinter.print(n.getChild(0), context.top().getSourceMap())
        + " of loop " + CAstPrinter.print(n, context.top().getSourceMap());
    context.cfg().addInstruction(
        insts.ConditionalBranchInstruction(context.cfg().currentInstruction, translateConditionOpcode(CAstOperator.OP_EQ), null, c.getValue(n.getChild(0)), context
            .currentScope().getConstantValue(new Integer(0))));
    PreBasicBlock branchB = context.cfg().getCurrentBlock();

    // loop body
    context.cfg().newBlock(true);
      context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
      PreBasicBlock bodyB = context.cfg().getCurrentBlock();
      context.cfg().addEdge(bodyB, headerB);

      // next block
      context.cfg().newBlock(false);
    }

    PreBasicBlock nextB = context.cfg().getCurrentBlock();

    // control flow mapping;
    context.cfg().addEdge(branchB, nextB);
    return true;
  }
  // Make final to prevent overriding
  protected final void leaveLoopHeader(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveLoop(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitGetCaughtException(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveGetCaughtException(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    String nm = (String) n.getChild(0).getValue();
    context.currentScope().declare(new FinalCAstSymbol(nm));
    context.cfg().addInstruction(
        insts.GetCaughtExceptionInstruction(context.cfg().currentInstruction, context.cfg().getCurrentBlock().getNumber(), context.currentScope()
            .lookup(nm).valueNumber()));
  }

  protected boolean visitThis(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveThis(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, 1);
  }

  protected boolean visitSuper(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveSuper(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, 1);
  }

  protected boolean visitCall(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveCall(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    int exp = context.currentScope().allocateTempValue();
    int fun = c.getValue(n.getChild(0));
    CAstNode functionName = n.getChild(1);
    int[] args = new int[n.getChildCount() - 2];
    for (int i = 0; i < args.length; i++) {
      args[i] = c.getValue(n.getChild(i + 2));
    }
    doCall(context, n, result, exp, functionName, fun, args);
  }

  protected boolean visitVar(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveVar(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    String nm = (String) n.getChild(0).getValue();
    assert nm != null : "cannot find var for " + CAstPrinter.print(n, context.getSourceMap());
    Symbol s = context.currentScope().lookup(nm);
    assert s != null : "cannot find symbol for " + nm + " at " + CAstPrinter.print(n, context.getSourceMap());
    if (context.currentScope().isGlobal(s)) {
      c.setValue(n, doGlobalRead(n, context, nm));
    } else if (context.currentScope().isLexicallyScoped(s)) {
      c.setValue(n, doLexicallyScopedRead(n, context, nm));
    } else {
      c.setValue(n, doLocalRead(context, nm));
    }
  }

  protected boolean visitConstant(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveConstant(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    c.setValue(n, context.currentScope().getConstantValue(n.getValue()));
  }

  protected boolean visitBinaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  private boolean handleBinaryOpThrow(CAstNode n, CAstNode op, WalkContext context) {
    // currently, only integer / and % throw exceptions
    boolean mayBeInteger = false;
    Collection labels = context.getControlFlow().getTargetLabels(n);
    if (!labels.isEmpty()) {
      context.cfg().addPreNode(n, context.getUnwindState());

      mayBeInteger = true;
      assert op == CAstOperator.OP_DIV || op == CAstOperator.OP_MOD : CAstPrinter.print(n);
      for (Iterator iter = labels.iterator(); iter.hasNext();) {
        Object label = iter.next();
        CAstNode target = context.getControlFlow().getTarget(n, label);
        if (target == CAstControlFlowMap.EXCEPTION_TO_EXIT)
          context.cfg().addPreEdgeToExit(n, true);
        else
          context.cfg().addPreEdge(n, target, true);
      }
    }

    return mayBeInteger;
  }

  protected void leaveBinaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    CAstNode l = n.getChild(1);
    CAstNode r = n.getChild(2);
    assert c.getValue(r) != -1 : CAstPrinter.print(n);
    assert c.getValue(l) != -1 : CAstPrinter.print(n);

    boolean mayBeInteger = handleBinaryOpThrow(n, n.getChild(0), context);

    context.cfg().addInstruction(
        insts.BinaryOpInstruction(context.cfg().currentInstruction, translateBinaryOpcode(n.getChild(0)), false, false, result, c.getValue(l), c.getValue(r),
            mayBeInteger));

    if (mayBeInteger) {
      context.cfg().newBlock(true);
    }
  }

  protected boolean visitUnaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveUnaryExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    CAstNode v = n.getChild(1);
    context.cfg().addInstruction(insts.UnaryOpInstruction(context.cfg().currentInstruction, translateUnaryOpcode(n.getChild(0)), result, c.getValue(v)));
  }

  protected boolean visitArrayLength(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveArrayLength(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    int arrayValue = c.getValue(n.getChild(0));
    context.cfg().addInstruction(insts.ArrayLengthInstruction(context.cfg().currentInstruction, result, arrayValue));
  }

  protected boolean visitArrayRef(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveArrayRef(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int arrayValue = c.getValue(n.getChild(0));
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    arrayOpHandler.doArrayRead(context, result, arrayValue, n, gatherArrayDims(c, n));
  }

  protected boolean visitDeclStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  // TODO: should we handle exploded declaration nodes here instead?
  protected void leaveDeclStmt(CAstNode n, WalkContext c, CAstVisitor visitor) {
    CAstSymbol s = (CAstSymbol) n.getChild(0).getValue();
    String nm = s.name();
    Scope scope = c.currentScope();
    if (n.getChildCount() == 2) {
      CAstNode v = n.getChild(1);
      if (scope.contains(nm) && scope.lookup(nm).getDefiningScope() == scope) {
        assert !s.isFinal();
        doLocalWrite(c, nm, c.getValue(v));
      } else if (v.getKind() != CAstNode.CONSTANT && v.getKind() != CAstNode.VAR && v.getKind() != CAstNode.THIS) {
        scope.declare(s, c.getValue(v));
      } else {
        scope.declare(s);
        doLocalWrite(c, nm, c.getValue(v));
      }
    } else {
      c.currentScope().declare(s);
    }
  }

  protected boolean visitReturn(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveReturn(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (n.getChildCount() > 0) {
      context.cfg().addInstruction(insts.ReturnInstruction(c.cfg().currentInstruction, c.getValue(n.getChild(0)), false));
    } else {
      context.cfg().addInstruction(insts.ReturnInstruction(context.cfg().currentInstruction));
    }

    context.cfg().addPreNode(n, context.getUnwindState());
    context.cfg().newBlock(false);
    context.cfg().addPreEdgeToExit(n, false);
  }

  protected boolean visitIfgoto(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveIfgoto(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (n.getChildCount() == 1) {
      context.cfg().addInstruction(
          insts.ConditionalBranchInstruction(c.cfg().currentInstruction, translateConditionOpcode(CAstOperator.OP_NE), null, c.getValue(n.getChild(0)), context
              .currentScope().getConstantValue(new Integer(0))));
    } else if (n.getChildCount() == 3) {
      context.cfg().addInstruction(
          insts.ConditionalBranchInstruction(c.cfg().currentInstruction, translateConditionOpcode(n.getChild(0)), null, c.getValue(n.getChild(1)),
              c.getValue(n.getChild(2))));
    } else {
      Assertions.UNREACHABLE();
    }

    context.cfg().addPreNode(n, context.getUnwindState());
    context.cfg().newBlock(true);
    context.cfg().addPreEdge(n, context.getControlFlow().getTarget(n, Boolean.TRUE), false);
  }

  protected boolean visitGoto(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveGoto(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
      context.cfg().addPreNode(n, context.getUnwindState());
      context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
      context.cfg().newBlock(false);
      if (context.getControlFlow().getTarget(n, null) == null) {
        assert context.getControlFlow().getTarget(n, null) != null : context.getControlFlow() + " does not map " + n + " ("
            + context.getSourceMap().getPosition(n) + ")";
      }
      context.cfg().addPreEdge(n, context.getControlFlow().getTarget(n, null), false);
    }
  }

  protected boolean visitLabelStmt(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (!context.getControlFlow().getSourceNodes(n).isEmpty()) {
      context.cfg().newBlock(true);
      arguments = null;
      context.cfg().addPreNode(n, context.getUnwindState());
    }
    return false;
  }

  protected void leaveLabelStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected void processIf(CAstNode n, boolean isExpr, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    PreBasicBlock trueB = null, falseB = null;
    // conditional
    CAstNode l = n.getChild(0);
    visitor.visit(l, context, visitor);
    context.cfg().addInstruction(
        insts.ConditionalBranchInstruction(c.cfg().currentInstruction, translateConditionOpcode(CAstOperator.OP_EQ), null, c.getValue(l), context.currentScope()
            .getConstantValue(new Integer(0))));
    PreBasicBlock srcB = context.cfg().getCurrentBlock();
    // true clause
    context.cfg().newBlock(true);
    CAstNode r = n.getChild(1);
    visitor.visit(r, context, visitor);
    if (isExpr)
      context.cfg().addInstruction(new AssignInstruction(c.cfg().currentInstruction, c.getValue(n), c.getValue(r)));
    if (n.getChildCount() == 3) {
      if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
        context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
        trueB = context.cfg().getCurrentBlock();

        // false clause
        context.cfg().newBlock(false);
      }

      falseB = context.cfg().getCurrentBlock();
      CAstNode f = n.getChild(2);
      visitor.visit(f, context, visitor);
      if (isExpr)
        context.cfg().addInstruction(new AssignInstruction(context.cfg().currentInstruction, c.getValue(n), c.getValue(f)));
    }

    // end
    context.cfg().newBlock(true);
    if (n.getChildCount() == 3) {
      if (trueB != null)
        context.cfg().addEdge(trueB, context.cfg().getCurrentBlock());
      context.cfg().addEdge(srcB, falseB);
    } else {
      context.cfg().addEdge(srcB, context.cfg().getCurrentBlock());
    }
  }

  // Make final to prevent overriding
  protected final void leaveIfStmtCondition(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfStmtTrueClause(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfExprCondition(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfExprTrueClause(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveIfExpr(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitIfStmt(CAstNode n, WalkContext c, CAstVisitor visitor) {
    processIf(n, false, c, visitor);
    return true;
  }

  protected boolean visitIfExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    processIf(n, true, c, visitor);
    return true;
  }

  protected boolean visitNew(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveNew(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;

    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);

    int[] arguments;
    if (n.getChildCount() <= 1) {
    } else {
      arguments = new int[n.getChildCount() - 1];
      for (int i = 1; i < n.getChildCount(); i++) {
        arguments[i - 1] = c.getValue(n.getChild(i));
      }
    }
    doNewObject(context, n, result, n.getChild(0).getValue(), arguments);
  }

  protected boolean visitObjectLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  }

  protected void leaveObjectLiteralFieldInit(CAstNode n, int i, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (n.getChild(i).getKind() == CAstNode.EMPTY) {
      handleUnspecifiedLiteralKey(context, n, i, visitor);
    }
    doFieldWrite(context, c.getValue(n.getChild(0)), n.getChild(i), n, c.getValue(n.getChild(i + 1)));
  }

  protected void leaveObjectLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(0)));
  }

  protected boolean visitArrayLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveArrayLiteralObject(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(0)));
  }

  protected void leaveArrayLiteralInitElement(CAstNode n, int i, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    arrayOpHandler.doArrayWrite(context, c.getValue(n.getChild(0)), n,
        new int[] { context.currentScope().getConstantValue(new Integer(i - 1)) }, c.getValue(n.getChild(i)));
  }

  protected void leaveArrayLiteral(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitObjectRef(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);
    return false;
  }

  protected void leaveObjectRef(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = c.getValue(n);
    CAstNode elt = n.getChild(1);
    doFieldRead(context, result, c.getValue(n.getChild(0)), elt, n);
  }

  public boolean visitAssign(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  public void leaveAssign(CAstNode n, WalkContext c, CAstVisitor visitor) {
    if (n.getKind() == CAstNode.ASSIGN) {
      c.setValue(n, c.getValue(n.getChild(1)));
    } else {
      c.setValue(n, c.getValue(n.getChild(0)));
    }
  }

  private int[] gatherArrayDims(WalkContext c, CAstNode n) {
    int numDims = n.getChildCount() - 2;
    int[] dims = new int[numDims];
    for (int i = 0; i < numDims; i++)
      dims[i] = c.getValue(n.getChild(i + 2));
    return dims;
  }

  /* Prereq: a.getKind() == ASSIGN_PRE_OP || a.getKind() == ASSIGN_POST_OP */
  protected int processAssignOp(CAstNode n, CAstNode v, CAstNode a, int temp, boolean post, WalkContext c) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    CAstNode op = a.getChild(2);
    int temp2 = context.currentScope().allocateTempValue();

    boolean mayBeInteger = handleBinaryOpThrow(a, op, context);

    context.cfg().addInstruction(
        insts.BinaryOpInstruction(context.cfg().currentInstruction, translateBinaryOpcode(op), false, false, temp2, temp, rval, mayBeInteger));

    if (mayBeInteger) {
      context.cfg().newBlock(true);
    }

    return temp2;
  }

  protected boolean visitArrayRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveArrayRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    c.setValue(n, rval);
    arrayOpHandler.doArrayWrite(context, c.getValue(n.getChild(0)), n, gatherArrayDims(c, n), rval);
  }

  protected boolean visitArrayRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveArrayRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int temp = context.currentScope().allocateTempValue();
    int[] dims = gatherArrayDims(c, n);
    arrayOpHandler.doArrayRead(context, temp, c.getValue(n.getChild(0)), n, dims);
    int rval = processAssignOp(n, v, a, temp, !pre, c);
    c.setValue(n, pre ? rval : temp);
    arrayOpHandler.doArrayWrite(context, c.getValue(n.getChild(0)), n, dims, rval);
  }

  protected boolean visitObjectRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveObjectRefAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    c.setValue(n, rval);
    doFieldWrite(context, c.getValue(n.getChild(0)), n.getChild(1), n, rval);
  }

  protected void processObjectRefAssignOp(CAstNode n, CAstNode v, CAstNode a, WalkContext c) {
  }

  protected boolean visitObjectRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveObjectRefAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int temp = context.currentScope().allocateTempValue();
    doFieldRead(context, temp, c.getValue(n.getChild(0)), n.getChild(1), n);
    int rval = processAssignOp(n, v, a, temp, !pre, c);
    c.setValue(n, pre ? rval : temp);
    doFieldWrite(context, c.getValue(n.getChild(0)), n.getChild(1), n, rval);
  }

  protected boolean visitBlockExprAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockExprAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, c.getValue(n.getChild(n.getChildCount() - 1)));
  }

  protected boolean visitBlockExprAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveBlockExprAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
    c.setValue(n, c.getValue(n.getChild(n.getChildCount() - 1)));
  }

  protected boolean visitVarAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  /**
   * assign rval to nm as appropriate, depending on the scope of ls
   */
  protected void assignValue(CAstNode n, WalkContext context, Symbol ls, String nm, int rval) {
    if (context.currentScope().isGlobal(ls))
      doGlobalWrite(context, nm, rval);
    else if (context.currentScope().isLexicallyScoped(ls)) {
      doLexicallyScopedWrite(context, nm, rval);
    } else {
      assert rval != -1 : CAstPrinter.print(n, context.top().getSourceMap());
      doLocalWrite(context, nm, rval);
    }
  }

  }

  protected void leaveVarAssign(CAstNode n, CAstNode v, CAstNode a, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int rval = c.getValue(v);
    String nm = (String) n.getChild(0).getValue();
    Symbol ls = context.currentScope().lookup(nm);
    c.setValue(n, rval);
    assignValue(n, context, ls, nm, rval);
  }

  protected boolean visitVarAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) { /* empty */
  protected void leaveVarAssignOp(CAstNode n, CAstNode v, CAstNode a, boolean pre, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    String nm = (String) n.getChild(0).getValue();
    Symbol ls = context.currentScope().lookup(nm);
    int temp;

    if (context.currentScope().isGlobal(ls))
      temp = doGlobalRead(n, context, nm);
    else if (context.currentScope().isLexicallyScoped(ls)) {
      temp = doLexicallyScopedRead(n, context, nm);
    } else {
      temp = doLocalRead(context, nm);
    }

    if (!pre) {
      int ret = context.currentScope().allocateTempValue();
      context.cfg().addInstruction(new AssignInstruction(c.cfg().currentInstruction, ret, temp));
      c.setValue(n, ret);
    }

    int rval = processAssignOp(n, v, a, temp, !pre, c);

    if (pre) {
      c.setValue(n, rval);
    }

    if (context.currentScope().isGlobal(ls)) {
      doGlobalWrite(context, nm, rval);
    } else if (context.currentScope().isLexicallyScoped(ls)) {
      doLexicallyScopedWrite(context, nm, rval);
    } else {
      doLocalWrite(context, nm, rval);
    }
  }

  private boolean isSimpleSwitch(CAstNode n, WalkContext context, CAstVisitor visitor) {
    CAstControlFlowMap ctrl = context.getControlFlow();
    Collection caseLabels = ctrl.getTargetLabels(n);
    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();

      if (x == CAstControlFlowMap.SWITCH_DEFAULT)
        continue;

      CAstNode xn = (CAstNode) x;
      if (xn.getKind() == CAstNode.CONSTANT) {
        visitor.visit(xn, context, visitor);
        if (context.getValue(xn) != -1) {
          if (context.currentScope().isConstant(context.getValue(xn))) {
            Object val = context.currentScope().getConstantObject(context.getValue(xn));
            if (val instanceof Number) {
              Number num = (Number) val;
              if ((double) num.intValue() == num.doubleValue()) {
                continue;
              }
            }
          }
        }
      }

      return false;
    }

    return true;
  }

  private void doSimpleSwitch(CAstNode n, WalkContext context, CAstVisitor visitor) {
    PreBasicBlock defaultHackBlock = null;
    CAstControlFlowMap ctrl = context.getControlFlow();

    CAstNode switchValue = n.getChild(0);
    visitor.visit(switchValue, context, visitor);
    int v = context.getValue(switchValue);

    boolean hasExplicitDefault = ctrl.getTarget(n, CAstControlFlowMap.SWITCH_DEFAULT) != null;

    Collection caseLabels = ctrl.getTargetLabels(n);
    int cases = caseLabels.size();
    if (hasExplicitDefault)
      cases--;
    int[] casesAndLabels = new int[cases * 2];

    int defaultBlock = context.cfg().getCurrentBlock().getGraphNodeId() + 1;

      addSkipCatchGoto = true;
      ;
    context.cfg().addInstruction(insts.SwitchInstruction(context.cfg().currentInstruction, v, defaultBlock, casesAndLabels));
    context.cfg().addPreNode(n, context.getUnwindState());
    // PreBasicBlock switchB = context.cfg().getCurrentBlock();
    context.cfg().newBlock(true);

    context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
    defaultHackBlock = context.cfg().getCurrentBlock();
    context.cfg().newBlock(false);

    CAstNode switchBody = n.getChild(1);
    visitor.visit(switchBody, context, visitor);
    context.cfg().newBlock(true);

    if (!hasExplicitDefault) {
      context.cfg().addEdge(defaultHackBlock, context.cfg().getCurrentBlock());
    }

    int cn = 0;
    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();
      CAstNode target = ctrl.getTarget(n, x);
      if (x == CAstControlFlowMap.SWITCH_DEFAULT) {
        context.cfg().addEdge(defaultHackBlock, context.cfg().getBlock(target));
      } else {
        Number caseLabel = (Number) context.currentScope().getConstantObject(context.getValue((CAstNode) x));
        casesAndLabels[2 * cn] = caseLabel.intValue();
        casesAndLabels[2 * cn + 1] = context.cfg().getBlock(target).getGraphNodeId();
        cn++;

        context.cfg().addPreEdge(n, target, false);
      }
    }
  }

  private void doIfConvertSwitch(CAstNode n, WalkContext context, CAstVisitor visitor) {
    CAstControlFlowMap ctrl = context.getControlFlow();
    context.cfg().addPreNode(n, context.getUnwindState());

    CAstNode switchValue = n.getChild(0);
    visitor.visit(switchValue, context, visitor);
    int v = context.getValue(switchValue);

    Collection caseLabels = ctrl.getTargetLabels(n);
    Map labelToBlock = new LinkedHashMap();
    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();
      if (x != CAstControlFlowMap.SWITCH_DEFAULT) {
        visitor.visit((CAstNode) x, context, visitor);
        context.cfg().addInstruction(
            insts.ConditionalBranchInstruction(context.cfg().currentInstruction, translateConditionOpcode(CAstOperator.OP_EQ), null, v, context.getValue((CAstNode) x)));
        labelToBlock.put(x, context.cfg().getCurrentBlock());
        context.cfg().newBlock(true);
      }
    }

    PreBasicBlock defaultGotoBlock = context.cfg().getCurrentBlock();
    context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
    context.cfg().newBlock(false);

    CAstNode switchBody = n.getChild(1);
    visitor.visit(switchBody, context, visitor);
    context.cfg().newBlock(true);

    for (Iterator kases = caseLabels.iterator(); kases.hasNext();) {
      Object x = kases.next();
      if (x != CAstControlFlowMap.SWITCH_DEFAULT) {
        CAstNode target = ctrl.getTarget(n, x);
        context.cfg().addEdge(labelToBlock.get(x), context.cfg().getBlock(target));
      }
    }

    if (ctrl.getTarget(n, CAstControlFlowMap.SWITCH_DEFAULT) == null) {
      context.cfg().addEdge(defaultGotoBlock, context.cfg().getCurrentBlock());
    } else {
      CAstNode target = ctrl.getTarget(n, CAstControlFlowMap.SWITCH_DEFAULT);
      context.cfg().addEdge(defaultGotoBlock, context.cfg().getBlock(target));
    }
  }

  protected boolean visitSwitch(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    if (isSimpleSwitch(n, context, visitor)) {
      doSimpleSwitch(n, context, visitor);
    } else {
      doIfConvertSwitch(n, context, visitor);
    }
    return true;
  }

  // Make final to prevent overriding
  protected final void leaveSwitchValue(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  protected final void leaveSwitch(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitThrow(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveThrow(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    doThrow(context, c.getValue(n.getChild(0)));

    context.cfg().addPreNode(n, context.getUnwindState());
    context.cfg().newBlock(false);

    Collection labels = context.getControlFlow().getTargetLabels(n);
    for (Iterator iter = labels.iterator(); iter.hasNext();) {
      Object label = iter.next();
      CAstNode target = context.getControlFlow().getTarget(n, label);
      if (target == CAstControlFlowMap.EXCEPTION_TO_EXIT)
        context.cfg().addPreEdgeToExit(n, true);
      else
        context.cfg().addPreEdge(n, target, true);
    }
  }

  protected boolean visitCatch(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;

    // unreachable catch block
    if (context.getControlFlow().getSourceNodes(n).isEmpty()) {
      return true;
    }

    String id = (String) n.getChild(0).getValue();
    context.cfg().setCurrentBlockAsHandler();
    if (!context.currentScope().contains(id)) {
      context.currentScope().declare(new FinalCAstSymbol(id));
    }
    context.cfg().addInstruction(
        insts.GetCaughtExceptionInstruction(context.cfg().currentInstruction, context.cfg().getCurrentBlock().getNumber(), context.currentScope()
            .lookup(id).valueNumber()));

    context.cfg().addPreNode(n, context.getUnwindState());

    CAstType caughtType = getTypeForNode(context, n);
    if (caughtType != null) {
      TypeReference caughtRef = makeType(caughtType);
      context.setCatchType(n, caughtRef);
    } else {
      context.setCatchType(n, defaultCatchType());
    }

    return false;
  }

  protected void leaveCatch(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitUnwind(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveUnwind(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  private boolean hasIncomingEdges(CAstNode n, WalkContext context) {
    if (context.cfg().hasDelayedEdges(n)) {
      return true;
    } else {
      for (int i = 0; i < n.getChildCount(); i++) {
        if (hasIncomingEdges(n.getChild(i), context)) {
          return true;
        }
      }

      return false;
    }
  }

  protected boolean visitTry(final CAstNode n, WalkContext c, CAstVisitor visitor) {
    final WalkContext context = (WalkContext) c;
    boolean addSkipCatchGoto = false;
    visitor.visit(n.getChild(0), context, visitor);
    PreBasicBlock endOfTry = context.cfg().getCurrentBlock();

    if (!hasIncomingEdges(n.getChild(1), context)) {
      if (loader instanceof CAstAbstractLoader) {
        ((CAstAbstractLoader) loader).addMessage(context.getModule(), new Warning(Warning.MILD) {
          @Override
          public String getMsg() {
            return "Dead catch block at " + getPosition(context.getSourceMap(), n.getChild(1));
          }
        });
      }
      return true;
    }

    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
      context.cfg().addInstruction(insts.GotoInstruction(context.cfg().currentInstruction));
      context.cfg().newBlock(false);
    }

    context.cfg().noteCatchBlock();
    visitor.visit(n.getChild(1), context, visitor);

    if (!context.cfg().isDeadBlock(context.cfg().getCurrentBlock())) {
      context.cfg().newBlock(true);
    }

    if (addSkipCatchGoto) {
      PreBasicBlock afterBlock = context.cfg().getCurrentBlock();
      context.cfg().addEdge(endOfTry, afterBlock);
    }
    return true;
  }

  // Make final to prevent overriding
  protected final void leaveTryBlock(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected final void leaveTry(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
  }

  protected boolean visitEmpty(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveEmpty(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    c.setValue(n, context.currentScope().getConstantValue(null));
  }

  protected boolean visitPrimitive(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leavePrimitive(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    int result = context.currentScope().allocateTempValue();
    c.setValue(n, result);

    doPrimitive(result, context, n);
  }

  protected boolean visitVoid(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveVoid(CAstNode n, WalkContext c, CAstVisitor visitor) {
    c.setValue(n, -1);
  }

  protected boolean visitAssert(CAstNode n, WalkContext c, CAstVisitor visitor) { /* empty */
    return false;
  }

  protected void leaveAssert(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext context = (WalkContext) c;
    boolean fromSpec = true;
    int result = c.getValue(n.getChild(0));
    if (n.getChildCount() == 2) {
      assert n.getChild(1).getKind() == CAstNode.CONSTANT;
      assert n.getChild(1).getValue() instanceof Boolean;
      fromSpec = n.getChild(1).getValue().equals(Boolean.TRUE);
    }
    context.cfg().addInstruction(new AstAssertInstruction(context.cfg().currentInstruction, result, fromSpec));
  }

  protected boolean visitEachElementGet(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  protected void leaveEachElementGet(CAstNode n, WalkContext c, CAstVisitor visitor) {
    int result = ((WalkContext) c).currentScope().allocateTempValue();
    c.setValue(n, result);
    ((WalkContext) c).cfg().addInstruction(new EachElementGetInstruction(c.cfg().currentInstruction, result, c.getValue(n.getChild(0))));
  }

  protected boolean visitEachElementHasNext(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  @Override
  protected void leaveEachElementHasNext(CAstNode n, WalkContext c, CAstVisitor visitor) {
    int result = ((WalkContext) c).currentScope().allocateTempValue();
    c.setValue(n, result);
    ((WalkContext) c).cfg().addInstruction(new EachElementHasNextInstruction(c.cfg().currentInstruction, result, c.getValue(n.getChild(0))));
  }

  protected boolean visitTypeLiteralExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  protected void leaveTypeLiteralExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;
    assert n.getChild(0).getKind() == CAstNode.CONSTANT;
    String typeNameStr = (String) n.getChild(0).getValue();
    TypeName typeName = TypeName.string2TypeName(typeNameStr);
    TypeReference typeRef = TypeReference.findOrCreate(loader.getReference(), typeName);

    int result = wc.currentScope().allocateTempValue();
    c.setValue(n, result);

    wc.cfg().addInstruction(insts.LoadMetadataInstruction(wc.cfg().currentInstruction, result, loader.getLanguage().getConstantType(typeRef), typeRef));
  }

  protected boolean visitIsDefinedExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  protected void leaveIsDefinedExpr(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;
    int ref = c.getValue(n.getChild(0));
    int result = wc.currentScope().allocateTempValue();
    c.setValue(n, result);
    if (n.getChildCount() == 1) {
      wc.cfg().addInstruction(new AstIsDefinedInstruction(wc.cfg().currentInstruction, result, ref));
    } else {
      doIsFieldDefined(wc, result, ref, n.getChild(1));
    }
  }

  protected boolean visitEcho(CAstNode n, WalkContext c, CAstVisitor visitor) {
    return false;
  }

  protected void leaveEcho(CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;

    int rvals[] = new int[n.getChildCount()];
    for (int i = 0; i < n.getChildCount(); i++) {
      rvals[i] = c.getValue(n.getChild(i));
    }

    wc.cfg().addInstruction(new AstEchoInstruction(wc.cfg().currentInstruction, rvals));
  }

  public CAstEntity getIncludedEntity(CAstNode n) {
    if (n.getChild(0).getKind() == CAstNode.NAMED_ENTITY_REF) {
      assert namedEntityResolver != null;
      return (CAstEntity) namedEntityResolver.get(n.getChild(0).getChild(0).getValue());
    } else {
      return (CAstEntity) n.getChild(0).getValue();
    }
  }

  protected void leaveInclude(final CAstNode n, WalkContext c, CAstVisitor visitor) {
    WalkContext wc = (WalkContext) c;

    CAstEntity included = getIncludedEntity(n);

    if (included == null) {
      System.err.println(("cannot find include for " + CAstPrinter.print(n)));
      System.err.println(("from:\n" + namedEntityResolver));
    } else {
      final boolean isMacroExpansion = (included.getKind() == CAstEntity.MACRO_ENTITY);

      System.err.println("found " + included.getName() + " for " + CAstPrinter.print(n));

      final CAstEntity copy = (new CAstCloner(new CAstImpl(), true) {

        private CAstNode copyIncludeExpr(CAstNode expr) {
          if (expr.getValue() != null) {
            return Ast.makeConstant(expr.getValue());
          } else if (expr instanceof CAstOperator) {
            return expr;
          } else {
            CAstNode nc[] = new CAstNode[expr.getChildCount()];

            for (int i = 0; i < expr.getChildCount(); i++) {
              nc[i] = copyIncludeExpr(expr.getChild(i));
            }

            return Ast.makeNode(expr.getKind(), nc);
          }
        }

        protected CAstNode copyNodes(CAstNode root, final CAstControlFlowMap cfg, NonCopyingContext c, Map, CAstNode> nodeMap) {
          if (isMacroExpansion && root.getKind() == CAstNode.MACRO_VAR) {
            int arg = ((Number) root.getChild(0).getValue()).intValue();
            CAstNode expr = copyIncludeExpr(n.getChild(arg));
            nodeMap.put(Pair.make(root, c.key()), expr);
            return expr;
          } else {
            return super.copyNodesHackForEclipse(root, cfg, c, nodeMap);
          }
        }
      }).rewrite(included);

      if (copy.getAST() == null) {
        System.err.println((copy.getName() + " has no AST"));

      } else {
        visit(copy.getAST(), new DelegatingContext(wc) {
          public CAstSourcePositionMap getSourceMap() {
            return copy.getSourceMap();
          }

          public CAstControlFlowMap getControlFlow() {
            return copy.getControlFlow();
          }
        }, visitor);

        visitor.visitScopedEntities(copy, copy.getAllScopedEntities(), wc, visitor);
      }
    }
  }

  protected final void walkEntities(CAstEntity N, WalkContext c) {
    visitEntities(N, c, this);
  }

  public final class RootContext implements WalkContext {
    private final Scope globalScope;
    
    private final CAstEntity N;

    private final ModuleEntry module;

    private final Map entityNames = new LinkedHashMap();

   public RootContext(CAstEntity N, ModuleEntry module) {
      this.N = N;
      this.module = module;
      this.globalScope = makeGlobalScope();
    }

    public ModuleEntry getModule() {
      return module;
    }

    public String file() {
      return module.getName();
    }

    public CAstEntity top() {
      return N;
    }

    public Scope currentScope() {
      return globalScope;
    }

    public Set entityScopes() {
      return Collections.singleton(globalScope);
    }

    public CAstSourcePositionMap getSourceMap() {
      return N.getSourceMap();
    }

    public CAstControlFlowMap getControlFlow() {
      return N.getControlFlow();
    }

    public IncipientCFG cfg() {
      return null;
    }

    public UnwindState getUnwindState() {
      return null;
    }

    public String getName() {
      return null;
    }

    public void setCatchType(int blockNumber, TypeReference catchType) {
    }

    public void setCatchType(CAstNode castNode, TypeReference catchType) {
    }

    public TypeReference[][] getCatchTypes() {
      return null;
    }
    
    public void addEntityName(CAstEntity e, String name) {
      entityNames.put(e, name);
    }

    public String getEntityName(CAstEntity e) {
      if (e == null) {
        return null;
      } else {
        assert entityNames.containsKey(e);
        return "L" + entityNames.get(e);
      }
    }

    public boolean hasValue(CAstNode n) {
      assert false;
      return false;
    }

    public int setValue(CAstNode n, int v) {
      assert false;
      return 0;
    }

    public int getValue(CAstNode n) {
      assert false;
      return -1;
    }

    public Set, Integer>> exposeNameSet(CAstEntity entity, boolean writeSet) {
      assert false;
      return null;
    }

    public Set getAccesses(CAstEntity e) {
      assert false;
      return null;
    }

    public Scope getGlobalScope(){
      return globalScope;
    }
    
  };

  /**
   * translate module, represented by {@link CAstEntity} N
   */
  public void translate(final CAstEntity N, final ModuleEntry module) {
    if (DEBUG_TOP)
      System.err.println(("translating " + module.getName()));
    // this.inlinedSourceMap = inlinedSourceMap;
    final ExposedNamesCollector exposedNamesCollector = new ExposedNamesCollector();
    exposedNamesCollector.run(N);
    entity2ExposedNames = exposedNamesCollector.getEntity2ExposedNames();
    // CAstEntity rewrite = (new ExposedParamRenamer(new CAstImpl(),
    // entity2ExposedNames)).rewrite(N);
    walkEntities(N, new RootContext(N, module));
  }

  public void translate(final CAstEntity N, final WalkContext context) {
    final ExposedNamesCollector exposedNamesCollector = new ExposedNamesCollector();
    exposedNamesCollector.run(N);
    entity2ExposedNames = exposedNamesCollector.getEntity2ExposedNames();
    walkEntities(N, context);
  }
 
}
File
AstTranslator.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.core.tests.cha;

import java.io.IOException;

import junit.framework.Assert;

import org.junit.Test;

import com.ibm.wala.core.tests.ir.DeterministicIRTest;
import com.ibm.wala.core.tests.util.TestConstants;
import com.ibm.wala.core.tests.util.WalaTestCase;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.util.config.AnalysisScopeReader;
import com.ibm.wala.util.io.FileProvider;

/**
 * Test code that attempts to find the library version from
 * the analysis scope.
 *
 * @author Julian Dolby (dolby@us.ibm.com)
 */
public class LibraryVersionTest extends WalaTestCase {
  
  private static final ClassLoader MY_CLASSLOADER = DeterministicIRTest.class.getClassLoader();

  @Test public void testLibraryVersion() throws IOException {
    AnalysisScope scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, (new FileProvider()).getFile("J2SEClassHierarchyExclusions.txt"), MY_CLASSLOADER);
    System.err.println("java library version is " + scope.getJavaLibraryVersion());
    Assert.assertTrue(scope.isJava17Libraries() || scope.isJava16Libraries() || scope.isJava15Libraries()||scope.isJava14Libraries());
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.core.tests.cha;

import java.io.IOException;

import junit.framework.Assert;

import org.junit.Test;

import com.ibm.wala.core.tests.ir.DeterministicIRTest;
import com.ibm.wala.core.tests.util.TestConstants;
import com.ibm.wala.core.tests.util.WalaTestCase;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.util.config.AnalysisScopeReader;
import com.ibm.wala.util.io.FileProvider;

/**
 * Test code that attempts to find the library version from
 * the analysis scope.
 *
 * @author Julian Dolby (dolby@us.ibm.com)
 */
public class LibraryVersionTest extends WalaTestCase {
  
  private static final ClassLoader MY_CLASSLOADER = DeterministicIRTest.class.getClassLoader();

  @Test public void testLibraryVersion() throws IOException {
    AnalysisScope scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, (new FileProvider()).getFile("J2SEClassHierarchyExclusions.txt"), MY_CLASSLOADER);
    System.err.println("java library version is " + scope.getJavaLibraryVersion());
    Assert.assertTrue(scope.isJava17Libraries() || scope.isJava16Libraries() || scope.isJava15Libraries()||scope.isJava14Libraries());
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.core.tests.cha;

import java.io.IOException;

import junit.framework.Assert;

import org.junit.Test;

import com.ibm.wala.core.tests.ir.DeterministicIRTest;
import com.ibm.wala.core.tests.util.TestConstants;
import com.ibm.wala.core.tests.util.WalaTestCase;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.util.config.AnalysisScopeReader;
import com.ibm.wala.util.io.FileProvider;

/**
 * Test code that attempts to find the library version from
 * the analysis scope.
 *
 * @author Julian Dolby (dolby@us.ibm.com)
 */
public class LibraryVersionTest extends WalaTestCase {
  
  private static final ClassLoader MY_CLASSLOADER = DeterministicIRTest.class.getClassLoader();

  @Test public void testLibraryVersion() throws IOException {
    AnalysisScope scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, (new FileProvider()).getFile("J2SEClassHierarchyExclusions.txt"), MY_CLASSLOADER);
    System.err.println("java library version is " + scope.getJavaLibraryVersion());
    Assert.assertTrue(scope.isJava17Libraries() || scope.isJava16Libraries() || scope.isJava15Libraries()||scope.isJava14Libraries());
  }

}
File
LibraryVersionTest.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;

import com.ibm.wala.analysis.typeInference.ConeType;
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.warnings.Warning;

/**
 * An abstract superclass of various {@link SSAContextInterpreter}s that deal with reflection methods.
 */
public abstract class AbstractReflectionInterpreter implements SSAContextInterpreter {

  protected static final boolean DEBUG = false;

  protected final static int CONE_BOUND = 10;

  protected int indexLocal = 100;

  protected final Map typeIndexMap = HashMapFactory.make();

  /**
   * Governing analysis options
   */
  protected AnalysisOptions options;

  /**
   * cache of analysis information
   */
  protected AnalysisCache cache;


  protected int getLocalForType(TypeReference T) {
    Integer I = typeIndexMap.get(T);
    if (I == null) {
      typeIndexMap.put(T, I = new Integer(indexLocal += 2));
    }
    return I.intValue();
  }

  protected int getExceptionsForType(TypeReference T) {
    return getLocalForType(T) + 1;
  }

  protected int getCallSiteForType(TypeReference T) {
    return getLocalForType(T);
  }

  protected int getNewSiteForType(TypeReference T) {
    return getLocalForType(T) + 1;
  }

  /**
   * @param type
   * @return a TypeAbstraction object representing this type. We just use ConeTypes by default, since we don't propagate
   *         information allowing us to distinguish between points and cones yet.
   */
  protected TypeAbstraction typeRef2TypeAbstraction(IClassHierarchy cha, TypeReference type) {
    IClass klass = cha.lookupClass(type);
    if (klass != null) {
      return new ConeType(klass);
    }
    Assertions.UNREACHABLE(type.toString());
    return null;
  }

  /**
   * A warning when we expect excessive pollution from a factory method
   */
  protected static class ManySubtypesWarning extends Warning {

    final int nImplementors;

    final TypeAbstraction T;

    ManySubtypesWarning(TypeAbstraction T, int nImplementors) {
      super(Warning.MODERATE);
      this.T = T;
      this.nImplementors = nImplementors;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + T + " " + nImplementors;
    }


    public static ManySubtypesWarning create(TypeAbstraction T, int n) {
      return new ManySubtypesWarning(T, n);
    }
  }

  /**
   * A warning when we fail to find subtypes for a factory method
   */
  protected static class NoSubtypesWarning extends Warning {

    final TypeAbstraction T;

    NoSubtypesWarning(TypeAbstraction T) {
      super(Warning.SEVERE);
      this.T = T;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + T;
    }

    public static NoSubtypesWarning create(TypeAbstraction T) {
      return new NoSubtypesWarning(T);
    }
  }

  /**
   * A warning when we find flow of a factory allocation to a cast to {@link Serializable}
   */
  protected static class IgnoreSerializableWarning extends Warning {

    final private static IgnoreSerializableWarning instance = new IgnoreSerializableWarning();

    @Override
    public String getMsg() {
      return getClass().toString();
    }

    public static IgnoreSerializableWarning create() {
      return instance;
    }
  }

  protected class SpecializedMethod extends SyntheticMethod {

    /**
     * Set of types that we have already inserted an allocation for.
     */
    protected final HashSet typesAllocated = HashSetFactory.make(5);

    /**
     * List of synthetic allocation statements we model for this specialized instance
     */
    final protected ArrayList allocations = new ArrayList();

    /**
     * List of synthetic invoke instructions we model for this specialized instance.
     */
    final protected ArrayList calls = new ArrayList();

    /**
     * List of all instructions
     */
    protected final ArrayList allInstructions = new ArrayList();

    private final SSAInstructionFactory insts = declaringClass.getClassLoader().getInstructionFactory();
    
    public SpecializedMethod(MethodReference method, IClass declaringClass, boolean isStatic, boolean isFactory) {
      super(method, declaringClass, isStatic, isFactory);
    }

    public SpecializedMethod(IMethod method, IClass declaringClass, boolean isStatic, boolean isFactory) {
      super(method, declaringClass, isStatic, isFactory);
    }

    /**
     * @param T type allocated by the instruction.   
     */
    protected void addInstruction(final TypeReference T, SSAInstruction instr, boolean isAllocation) {
      if (isAllocation) {
        if (typesAllocated.contains(T)) {
          return;
        } else {
          typesAllocated.add(T);
        }
      }

      allInstructions.add(instr);
      if (isAllocation) {
        allocations.add(instr);
      }
    }

    /**
     * @param t type of object to allocate
     * @return value number of the newly allocated object
     */
    protected int addStatementsForConcreteSimpleType(final TypeReference t) {
      // assert we haven't allocated this type already.
      assert !typesAllocated.contains(t);
      if (DEBUG) {
        System.err.println(("addStatementsForConcreteType: " + t));
      }
  protected int indexLocal = 100;
      NewSiteReference ref = NewSiteReference.make(getNewSiteForType(t), t);
      int alloc = getLocalForType(t);

      if (t.isArrayType()) {
        // for now, just allocate an array of size 1 in each dimension.
        int dim = t.getDimensionality();
        int[] extents = new int[dim];
        Arrays.fill(extents, 1);
        SSANewInstruction a = insts.NewInstruction(allInstructions.size(), alloc, ref, extents);
        addInstruction(t, a, true);
      } else {
        SSANewInstruction a = insts.NewInstruction(allInstructions.size(), alloc, ref);
        addInstruction(t, a, true);
        addCtorInvokeInstruction(t, alloc);
      }

      SSAReturnInstruction r = insts.ReturnInstruction(allInstructions.size(), alloc, false);
      addInstruction(t, r, false);
      return alloc;
    }

    /**
     * Add an instruction to invoke the default constructor on the object of value number alloc of type t.
     */
    protected void addCtorInvokeInstruction(final TypeReference t, int alloc) {
      MethodReference init = MethodReference.findOrCreate(t, MethodReference.initAtom, MethodReference.defaultInitDesc);
      CallSiteReference site = CallSiteReference.make(getCallSiteForType(t), init, IInvokeInstruction.Dispatch.SPECIAL);
      int[] params = new int[1];
      params[0] = alloc;
      int exc = getExceptionsForType(t);
      SSAInvokeInstruction s = insts.InvokeInstruction(allInstructions.size(), params, exc, site);
      calls.add(s);
      allInstructions.add(s);
    }
  }
=======
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import static com.ibm.wala.types.TypeName.ArrayMask;
import static com.ibm.wala.types.TypeName.ElementMask;
import static com.ibm.wala.types.TypeName.PrimitiveMask;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;

import com.ibm.wala.analysis.typeInference.ConeType;
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.warnings.Warning;

/**
 * An abstract superclass of various {@link SSAContextInterpreter}s that deal with reflection methods.
 */
public abstract class AbstractReflectionInterpreter implements SSAContextInterpreter {

  protected static final boolean DEBUG = false;

  protected final static int CONE_BOUND = 10;

  protected final Map typeIndexMap = HashMapFactory.make();

  /**
   * Governing analysis options
   */
  protected AnalysisOptions options;

  /**
   * cache of analysis information
   */
  protected AnalysisCache cache;


  protected int getLocalForType(TypeReference T) {
    Integer I = typeIndexMap.get(T);
    if (I == null) {
      typeIndexMap.put(T, I = new Integer(indexLocal += 2));
    }
    return I.intValue();
  }

  protected int getExceptionsForType(TypeReference T) {
    return getLocalForType(T) + 1;
  }

  protected int getCallSiteForType(TypeReference T) {
    return getLocalForType(T);
  }

  protected int getNewSiteForType(TypeReference T) {
    return getLocalForType(T) + 1;
  }

  /**
   * @param type
   * @return a TypeAbstraction object representing this type. We just use ConeTypes by default, since we don't propagate
   *         information allowing us to distinguish between points and cones yet.
   */
  protected TypeAbstraction typeRef2TypeAbstraction(IClassHierarchy cha, TypeReference type) {
    IClass klass = cha.lookupClass(type);
    if (klass != null) {
      return new ConeType(klass);
    }
    Assertions.UNREACHABLE(type.toString());
    return null;

  /**
   * A warning when we expect excessive pollution from a factory method
   */
  protected static class ManySubtypesWarning extends Warning {

    final int nImplementors;

    final TypeAbstraction T;

    ManySubtypesWarning(TypeAbstraction T, int nImplementors) {
      super(Warning.MODERATE);
      this.T = T;
      this.nImplementors = nImplementors;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + T + " " + nImplementors;
    }

    public static ManySubtypesWarning create(TypeAbstraction T, int n) {
      return new ManySubtypesWarning(T, n);
    }
  }

  /**
   * A warning when we fail to find subtypes for a factory method
   */
  protected static class NoSubtypesWarning extends Warning {

    final TypeAbstraction T;

    NoSubtypesWarning(TypeAbstraction T) {
      super(Warning.SEVERE);
      this.T = T;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + T;
    }

    public static NoSubtypesWarning create(TypeAbstraction T) {
      return new NoSubtypesWarning(T);
    }
  }

  /**
   * A warning when we find flow of a factory allocation to a cast to {@link Serializable}
   */
  protected static class IgnoreSerializableWarning extends Warning {

    final private static IgnoreSerializableWarning instance = new IgnoreSerializableWarning();

    @Override
    public String getMsg() {
      return getClass().toString();
    }

    public static IgnoreSerializableWarning create() {
      return instance;
    }
  }

  protected class SpecializedMethod extends SyntheticMethod {

    /**
     * Set of types that we have already inserted an allocation for.
     */
    protected final HashSet typesAllocated = HashSetFactory.make(5);

    /**
     * List of synthetic allocation statements we model for this specialized instance
     */
    final protected ArrayList allocations = new ArrayList();

    /**
     * List of synthetic invoke instructions we model for this specialized instance.
     */
    final protected ArrayList calls = new ArrayList();

    /**
     * List of all instructions
     */
    protected final ArrayList allInstructions = new ArrayList();

    private final SSAInstructionFactory insts = declaringClass.getClassLoader().getInstructionFactory();
    
    public SpecializedMethod(MethodReference method, IClass declaringClass, boolean isStatic, boolean isFactory) {
      super(method, declaringClass, isStatic, isFactory);
    }

    public SpecializedMethod(IMethod method, IClass declaringClass, boolean isStatic, boolean isFactory) {
      super(method, declaringClass, isStatic, isFactory);
    }

    /**
     * @param T type allocated by the instruction.   
     */
    protected void addInstruction(final TypeReference T, SSAInstruction instr, boolean isAllocation) {
      if (isAllocation) {
        if (typesAllocated.contains(T)) {
          return;
        } else {
          typesAllocated.add(T);
        }
      }

      allInstructions.add(instr);
      if (isAllocation) {
        allocations.add(instr);
      }
    }

    /**
     * @param t type of object to allocate
     * @return value number of the newly allocated object
     */
    protected int addStatementsForConcreteSimpleType(final TypeReference t) {
      // assert we haven't allocated this type already.
      assert !typesAllocated.contains(t);
      if (DEBUG) {
        System.err.println(("addStatementsForConcreteType: " + t));
      }
      NewSiteReference ref = NewSiteReference.make(getNewSiteForType(t), t);
      int alloc = getLocalForType(t);

      if (t.isArrayType()) {
        // for now, just allocate an array of size 1 in each dimension.
        int dims = 0;
        int dim = t.getDerivedMask();
        if ((dim&ElementMask) == PrimitiveMask) {
          dim >>= 2;
        }
        while ((dim&ElementMask) == ArrayMask) {
          dims++;
          dim >>=2;
        }
        
        int[] extents = new int[dims];
        Arrays.fill(extents, 1);
        SSANewInstruction a = insts.NewInstruction(alloc, ref, extents);
        addInstruction(t, a, true);
      } else {
        SSANewInstruction a = insts.NewInstruction(alloc, ref);
        addInstruction(t, a, true);
        addCtorInvokeInstruction(t, alloc);
      }

      SSAReturnInstruction r = insts.ReturnInstruction(alloc, false);
      addInstruction(t, r, false);
      return alloc;
    }

    /**
     * Add an instruction to invoke the default constructor on the object of value number alloc of type t.
     */
    protected void addCtorInvokeInstruction(final TypeReference t, int alloc) {
      MethodReference init = MethodReference.findOrCreate(t, MethodReference.initAtom, MethodReference.defaultInitDesc);
      CallSiteReference site = CallSiteReference.make(getCallSiteForType(t), init, IInvokeInstruction.Dispatch.SPECIAL);
      int[] params = new int[1];
      params[0] = alloc;
      int exc = getExceptionsForType(t);
      SSAInvokeInstruction s = insts.InvokeInstruction(params, exc, site);
      calls.add(s);
      allInstructions.add(s);
    }
  }
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
}
Solution content
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import static com.ibm.wala.types.TypeName.ArrayMask;
import static com.ibm.wala.types.TypeName.ElementMask;
import static com.ibm.wala.types.TypeName.PrimitiveMask;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;

import com.ibm.wala.analysis.typeInference.ConeType;
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.warnings.Warning;

/**
 * An abstract superclass of various {@link SSAContextInterpreter}s that deal with reflection methods.
 */
public abstract class AbstractReflectionInterpreter implements SSAContextInterpreter {

  protected static final boolean DEBUG = false;

  protected final static int CONE_BOUND = 10;

  protected int indexLocal = 100;

  protected final Map typeIndexMap = HashMapFactory.make();

  /**
   * Governing analysis options
   */
  protected AnalysisOptions options;

  /**
   * cache of analysis information
   */
  protected AnalysisCache cache;


  protected int getLocalForType(TypeReference T) {
    Integer I = typeIndexMap.get(T);
    if (I == null) {
      typeIndexMap.put(T, I = new Integer(indexLocal += 2));
    }
    return I.intValue();
  }

  protected int getExceptionsForType(TypeReference T) {
    return getLocalForType(T) + 1;
  }

  protected int getCallSiteForType(TypeReference T) {
    return getLocalForType(T);
  }

  protected int getNewSiteForType(TypeReference T) {
    return getLocalForType(T) + 1;
  }

  /**
   * @param type
   * @return a TypeAbstraction object representing this type. We just use ConeTypes by default, since we don't propagate
   *         information allowing us to distinguish between points and cones yet.
   */
  protected TypeAbstraction typeRef2TypeAbstraction(IClassHierarchy cha, TypeReference type) {
    IClass klass = cha.lookupClass(type);
    if (klass != null) {
      return new ConeType(klass);
    }
    Assertions.UNREACHABLE(type.toString());
    return null;
  }

  /**
   * A warning when we expect excessive pollution from a factory method
   */
  protected static class ManySubtypesWarning extends Warning {

    final int nImplementors;

    final TypeAbstraction T;

    ManySubtypesWarning(TypeAbstraction T, int nImplementors) {
      super(Warning.MODERATE);
      this.T = T;
      this.nImplementors = nImplementors;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + T + " " + nImplementors;
    }

    public static ManySubtypesWarning create(TypeAbstraction T, int n) {
      return new ManySubtypesWarning(T, n);
    }
  }

  /**
   * A warning when we fail to find subtypes for a factory method
   */
  protected static class NoSubtypesWarning extends Warning {

    final TypeAbstraction T;

    NoSubtypesWarning(TypeAbstraction T) {
      super(Warning.SEVERE);
      this.T = T;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + T;
    }

    public static NoSubtypesWarning create(TypeAbstraction T) {
      return new NoSubtypesWarning(T);
    }
  }

  /**
   * A warning when we find flow of a factory allocation to a cast to {@link Serializable}
   */
  protected static class IgnoreSerializableWarning extends Warning {

    final private static IgnoreSerializableWarning instance = new IgnoreSerializableWarning();

    @Override
    public String getMsg() {
      return getClass().toString();
    }

    public static IgnoreSerializableWarning create() {
      return instance;
    }
  }

  protected class SpecializedMethod extends SyntheticMethod {

    /**
     * Set of types that we have already inserted an allocation for.
     */
    protected final HashSet typesAllocated = HashSetFactory.make(5);

    /**
     * List of synthetic allocation statements we model for this specialized instance
     */
    final protected ArrayList allocations = new ArrayList();

    /**
     * List of synthetic invoke instructions we model for this specialized instance.
     */
    final protected ArrayList calls = new ArrayList();

    /**
     * List of all instructions
     */
    protected final ArrayList allInstructions = new ArrayList();

    private final SSAInstructionFactory insts = declaringClass.getClassLoader().getInstructionFactory();
    
    public SpecializedMethod(MethodReference method, IClass declaringClass, boolean isStatic, boolean isFactory) {
      super(method, declaringClass, isStatic, isFactory);
    }

    public SpecializedMethod(IMethod method, IClass declaringClass, boolean isStatic, boolean isFactory) {
      super(method, declaringClass, isStatic, isFactory);
    }

    /**
     * @param T type allocated by the instruction.   
     */
    protected void addInstruction(final TypeReference T, SSAInstruction instr, boolean isAllocation) {
      if (isAllocation) {
        if (typesAllocated.contains(T)) {
          return;
        } else {
          typesAllocated.add(T);
        }
      }

      allInstructions.add(instr);
      if (isAllocation) {
        allocations.add(instr);
      }
    }

    /**
     * @param t type of object to allocate
     * @return value number of the newly allocated object
     */
    protected int addStatementsForConcreteSimpleType(final TypeReference t) {
      // assert we haven't allocated this type already.
      assert !typesAllocated.contains(t);
      if (DEBUG) {
        System.err.println(("addStatementsForConcreteType: " + t));
      }
      NewSiteReference ref = NewSiteReference.make(getNewSiteForType(t), t);
      int alloc = getLocalForType(t);

      if (t.isArrayType()) {
        // for now, just allocate an array of size 1 in each dimension.
        int dims = 0;
        int dim = t.getDerivedMask();
        if ((dim&ElementMask) == PrimitiveMask) {
          dim >>= 2;
        }
        while ((dim&ElementMask) == ArrayMask) {
          dims++;
          dim >>=2;
        }
        
        int[] extents = new int[dims];
        Arrays.fill(extents, 1);
        SSANewInstruction a = insts.NewInstruction(allInstructions.size(), alloc, ref, extents);
        addInstruction(t, a, true);
      } else {
        SSANewInstruction a = insts.NewInstruction(allInstructions.size(), alloc, ref);
        addInstruction(t, a, true);
        addCtorInvokeInstruction(t, alloc);
      }

      SSAReturnInstruction r = insts.ReturnInstruction(allInstructions.size(), alloc, false);
      addInstruction(t, r, false);
      return alloc;
    }

    /**
     * Add an instruction to invoke the default constructor on the object of value number alloc of type t.
     */
    protected void addCtorInvokeInstruction(final TypeReference t, int alloc) {
      MethodReference init = MethodReference.findOrCreate(t, MethodReference.initAtom, MethodReference.defaultInitDesc);
      CallSiteReference site = CallSiteReference.make(getCallSiteForType(t), init, IInvokeInstruction.Dispatch.SPECIAL);
      int[] params = new int[1];
      params[0] = alloc;
      int exc = getExceptionsForType(t);
      SSAInvokeInstruction s = insts.InvokeInstruction(allInstructions.size(), params, exc, site);
      calls.add(s);
      allInstructions.add(s);
    }
  }
}
File
AbstractReflectionInterpreter.java
Developer's decision
Combination
Kind of conflict
Attribute
Class declaration
Comment
Import
Method declaration
Method invocation
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;

/**
 * An {@link SSAContextInterpreter} specialized to interpret reflective class factories (e.g. Class.forName()) in a
 * {@link JavaTypeContext} which represents the point-type of the class object created by the call.
 */
public class ClassFactoryContextInterpreter implements SSAContextInterpreter {

  private static final boolean DEBUG = false;

/** BEGIN Custom change: caching */
  private final Map cache = HashMapFactory.make();
  
/** END Custom change: caching */
  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
/** BEGIN Custom change: caching */
    
    final JavaTypeContext context = (JavaTypeContext) node.getContext();
    final IMethod method = node.getMethod();
    final String hashKey = method.toString() + "@" + context.toString();
    
    IR result = cache.get(hashKey);
    
    if (result == null) {
      result = makeIR(method, context);
      cache.put(hashKey, result);
    }
    
/** END Custom change: caching */
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (!(node.getContext() instanceof JavaTypeContext)) {
      return false;
    }
    return ClassFactoryContextSelector.isClassFactory(node.getMethod().getReference());
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateNewSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    JavaTypeContext context = (JavaTypeContext) node.getContext();
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      return new NonNullSingletonIterator(NewSiteReference.make(0, tr));
    }
    return EmptyIterator.instance();
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateCallSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return EmptyIterator.instance();
  }

  private SSAInstruction[] makeStatements(JavaTypeContext context) {
    SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory();
    ArrayList statements = new ArrayList();
    // vn1 is the string parameter
    int retValue = 2;
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      SSALoadMetadataInstruction l = insts.LoadMetadataInstruction(statements.size(), retValue, TypeReference.JavaLangClass, tr);
      statements.add(l);
      SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), retValue, false);
      statements.add(R);
    } else {
      SSAThrowInstruction t = insts.ThrowInstruction(statements.size(), retValue);
      statements.add(t);
    }
    SSAInstruction[] result = new SSAInstruction[statements.size()];
    statements.toArray(result);
    return result;
  }

  private IR makeIR(IMethod method, JavaTypeContext context) {
    SSAInstruction instrs[] = makeStatements(context);
    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Iterator;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.NonNullSingletonIterator;

/**
 * An {@link SSAContextInterpreter} specialized to interpret reflective class factories (e.g. Class.forName()) in a
 * {@link JavaTypeContext} which represents the point-type of the class object created by the call.
 */
public class ClassFactoryContextInterpreter implements SSAContextInterpreter {

  private static final boolean DEBUG = false;

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
    IR result = makeIR(node.getMethod(), (JavaTypeContext) node.getContext());
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (!(node.getContext() instanceof JavaTypeContext)) {
      return false;
    }
    return ClassFactoryContextSelector.isClassFactory(node.getMethod().getReference());
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateNewSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    JavaTypeContext context = (JavaTypeContext) node.getContext();
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      return new NonNullSingletonIterator(NewSiteReference.make(0, tr));
    }
    return EmptyIterator.instance();
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateCallSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return EmptyIterator.instance();
  }

  private SSAInstruction[] makeStatements(JavaTypeContext context) {
    SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory();
    ArrayList statements = new ArrayList();
    // vn1 is the string parameter
    int retValue = 2;
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      SSALoadMetadataInstruction l = insts.LoadMetadataInstruction(retValue, TypeReference.JavaLangClass, tr);
      statements.add(l);
      SSAReturnInstruction R = insts.ReturnInstruction(retValue, false);
      statements.add(R);
    } else {
      SSAThrowInstruction t = insts.ThrowInstruction(retValue);
      statements.add(t);
    }
    SSAInstruction[] result = new SSAInstruction[statements.size()];
    statements.toArray(result);
    return result;
  }

  private IR makeIR(IMethod method, JavaTypeContext context) {
    SSAInstruction instrs[] = makeStatements(context);
    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
      return false;
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;

/**
 * An {@link SSAContextInterpreter} specialized to interpret reflective class factories (e.g. Class.forName()) in a
 * {@link JavaTypeContext} which represents the point-type of the class object created by the call.
 */
public class ClassFactoryContextInterpreter implements SSAContextInterpreter {

  private static final boolean DEBUG = false;

/** BEGIN Custom change: caching */
  private final Map cache = HashMapFactory.make();
  
/** END Custom change: caching */
  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
/** BEGIN Custom change: caching */
    
    final JavaTypeContext context = (JavaTypeContext) node.getContext();
    final IMethod method = node.getMethod();
    final String hashKey = method.toString() + "@" + context.toString();
    
    IR result = cache.get(hashKey);
    
    if (result == null) {
      result = makeIR(method, context);
      cache.put(hashKey, result);
    }
    
/** END Custom change: caching */
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (!(node.getContext() instanceof JavaTypeContext)) {
    }
    return ClassFactoryContextSelector.isClassFactory(node.getMethod().getReference());
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateNewSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    JavaTypeContext context = (JavaTypeContext) node.getContext();
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      return new NonNullSingletonIterator(NewSiteReference.make(0, tr));
    }
    return EmptyIterator.instance();
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateCallSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return EmptyIterator.instance();
  }

  private SSAInstruction[] makeStatements(JavaTypeContext context) {
    SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory();
    ArrayList statements = new ArrayList();
    // vn1 is the string parameter
    int retValue = 2;
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      SSALoadMetadataInstruction l = insts.LoadMetadataInstruction(statements.size(), retValue, TypeReference.JavaLangClass, tr);
      statements.add(l);
      SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), retValue, false);
      statements.add(R);
    } else {
      SSAThrowInstruction t = insts.ThrowInstruction(statements.size(), retValue);
      statements.add(t);
    }
    SSAInstruction[] result = new SSAInstruction[statements.size()];
    statements.toArray(result);
    return result;
  }

  private IR makeIR(IMethod method, JavaTypeContext context) {
    SSAInstruction instrs[] = makeStatements(context);
    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
File
ClassFactoryContextInterpreter.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
 * Contributors:
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2008 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
 *
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.ibm.wala.analysis.reflection;

import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.strings.Atom;

/**
 * An {@link SSAContextInterpreter} specialized to interpret Class.newInstance in a {@link JavaTypeContext} which represents the
 * point-type of the class object created by the call.
 */
public class ClassNewInstanceContextInterpreter extends AbstractReflectionInterpreter {

  public final static Atom newInstanceAtom = Atom.findOrCreateUnicodeAtom("newInstance");

  private final static Descriptor classNewInstanceDescriptor = Descriptor.findOrCreateUTF8("()Ljava/lang/Object;");

  public final static MethodReference CLASS_NEW_INSTANCE_REF = MethodReference.findOrCreate(TypeReference.JavaLangClass,
      newInstanceAtom, classNewInstanceDescriptor);

  private final static Atom defCtorAtom = Atom.findOrCreateUnicodeAtom("");

  private final static Descriptor defCtorDescriptor = Descriptor.findOrCreateUTF8("()V");

  private final static Selector defCtorSelector = new Selector(defCtorAtom, defCtorDescriptor);

  private final IClassHierarchy cha;

/** BEGIN Custom change: caching */
  private final Map cache = HashMapFactory.make();
  
/** END Custom change: caching */
  public ClassNewInstanceContextInterpreter(IClassHierarchy cha) {
    this.cha = cha;
  }

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
/** BEGIN Custom change: caching */
    
    final JavaTypeContext context = (JavaTypeContext) node.getContext();
    final IMethod method = node.getMethod();
    final String hashKey = method.toString() + "@" + context.toString();
    
    IR result = cache.get(hashKey);
    
    if (result == null) {
      result = makeIR(method, context);
      cache.put(hashKey, result);
    }
    
/** END Custom change: caching */
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (!(node.getContext() instanceof JavaTypeContext)) {
      return false;
    }
    return node.getMethod().getReference().equals(CLASS_NEW_INSTANCE_REF);
  }

  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    JavaTypeContext context = (JavaTypeContext) node.getContext();
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      return new NonNullSingletonIterator(NewSiteReference.make(0, tr));
    }
    return EmptyIterator.instance();
  }

  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return EmptyIterator.instance();
  }

  private IR makeIR(IMethod method, JavaTypeContext context) {
    SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory();
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      SpecializedMethod m = new SpecializedMethod(method, method.getDeclaringClass(), method.isStatic(), false);
      IClass klass = cha.lookupClass(tr);
      IMethod publicDefaultCtor = getPublicDefaultCtor(klass);
      if (publicDefaultCtor != null) {
        m.addStatementsForConcreteSimpleType(tr);
      } else if (klass.getMethod(defCtorSelector) == null) {
        TypeReference instantiationExceptionRef = TypeReference.findOrCreateClass(ClassLoaderReference.Primordial, "java/lang",
            "InstantiationException");
        int xobj = method.getNumberOfParameters() + 1;
        SSAInstruction newStatement = insts.NewInstruction(m.allInstructions.size(), xobj, NewSiteReference.make(2, instantiationExceptionRef));
        m.addInstruction(tr, newStatement, true);
        SSAInstruction throwStatement = insts.ThrowInstruction(m.allInstructions.size(), xobj);
        m.addInstruction(tr, throwStatement, false);
      } else {
        TypeReference illegalAccessExceptionRef = TypeReference.findOrCreateClass(ClassLoaderReference.Primordial, "java/lang",
            "IllegalAccessException");
        int xobj = method.getNumberOfParameters() + 1;
        SSAInstruction newStatement = insts.NewInstruction(m.allInstructions.size(), xobj, NewSiteReference.make(2, illegalAccessExceptionRef));
        m.addInstruction(tr, newStatement, true);
        SSAInstruction throwStatement = insts.ThrowInstruction(m.allInstructions.size(), xobj);
        m.addInstruction(tr, throwStatement, false);
      }

      SSAInstruction[] instrs = new SSAInstruction[m.allInstructions.size()];
      m.allInstructions. toArray(instrs);
      return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
    }

    return null;
  }

  private IMethod getPublicDefaultCtor(IClass klass) {
    IMethod ctorMethod = klass.getMethod(defCtorSelector);
    if (ctorMethod != null && ctorMethod.isPublic()) {
      return ctorMethod;
    }
    return null;
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.Iterator;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.strings.Atom;

/**
 * An {@link SSAContextInterpreter} specialized to interpret Class.newInstance in a {@link JavaTypeContext} which represents the
 * point-type of the class object created by the call.
 */
public class ClassNewInstanceContextInterpreter extends AbstractReflectionInterpreter {

  public final static Atom newInstanceAtom = Atom.findOrCreateUnicodeAtom("newInstance");

  private final static Descriptor classNewInstanceDescriptor = Descriptor.findOrCreateUTF8("()Ljava/lang/Object;");

  public final static MethodReference CLASS_NEW_INSTANCE_REF = MethodReference.findOrCreate(TypeReference.JavaLangClass,
      newInstanceAtom, classNewInstanceDescriptor);

  private final static Atom defCtorAtom = Atom.findOrCreateUnicodeAtom("");

  private final static Descriptor defCtorDescriptor = Descriptor.findOrCreateUTF8("()V");

  private final static Selector defCtorSelector = new Selector(defCtorAtom, defCtorDescriptor);

  private final IClassHierarchy cha;

  public ClassNewInstanceContextInterpreter(IClassHierarchy cha) {
    this.cha = cha;
  }

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
    IR result = makeIR(node.getMethod(), (JavaTypeContext) node.getContext());
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (!(node.getContext() instanceof JavaTypeContext)) {
      return false;
    }
    return node.getMethod().getReference().equals(CLASS_NEW_INSTANCE_REF);
  }

  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    JavaTypeContext context = (JavaTypeContext) node.getContext();
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      return new NonNullSingletonIterator(NewSiteReference.make(0, tr));
    }
    return EmptyIterator.instance();
  }

  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return EmptyIterator.instance();
  }

  private IR makeIR(IMethod method, JavaTypeContext context) {
    SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory();
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      SpecializedMethod m = new SpecializedMethod(method, method.getDeclaringClass(), method.isStatic(), false);
      IClass klass = cha.lookupClass(tr);
      IMethod publicDefaultCtor = getPublicDefaultCtor(klass);
      if (publicDefaultCtor != null) {
        m.addStatementsForConcreteSimpleType(tr);
      } else if (klass.getMethod(defCtorSelector) == null) {
        TypeReference instantiationExceptionRef = TypeReference.findOrCreateClass(ClassLoaderReference.Primordial, "java/lang",
            "InstantiationException");
        int xobj = method.getNumberOfParameters() + 1;
        SSAInstruction newStatement = insts.NewInstruction(xobj, NewSiteReference.make(2, instantiationExceptionRef));
        m.addInstruction(tr, newStatement, true);
        SSAInstruction throwStatement = insts.ThrowInstruction(xobj);
        m.addInstruction(tr, throwStatement, false);
      } else {
        TypeReference illegalAccessExceptionRef = TypeReference.findOrCreateClass(ClassLoaderReference.Primordial, "java/lang",
            "IllegalAccessException");
        int xobj = method.getNumberOfParameters() + 1;
        SSAInstruction newStatement = insts.NewInstruction(xobj, NewSiteReference.make(2, illegalAccessExceptionRef));
        m.addInstruction(tr, newStatement, true);
        SSAInstruction throwStatement = insts.ThrowInstruction(xobj);
        m.addInstruction(tr, throwStatement, false);
      }

      SSAInstruction[] instrs = new SSAInstruction[m.allInstructions.size()];
      m.allInstructions. toArray(instrs);
      return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
    }

    return null;
  }

  private IMethod getPublicDefaultCtor(IClass klass) {
    IMethod ctorMethod = klass.getMethod(defCtorSelector);
    if (ctorMethod != null && ctorMethod.isPublic()) {
      return ctorMethod;
    }
    return null;
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
    }
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.strings.Atom;

/**
 * An {@link SSAContextInterpreter} specialized to interpret Class.newInstance in a {@link JavaTypeContext} which represents the
 * point-type of the class object created by the call.
 */
public class ClassNewInstanceContextInterpreter extends AbstractReflectionInterpreter {

  public final static Atom newInstanceAtom = Atom.findOrCreateUnicodeAtom("newInstance");

  private final static Descriptor classNewInstanceDescriptor = Descriptor.findOrCreateUTF8("()Ljava/lang/Object;");

  public final static MethodReference CLASS_NEW_INSTANCE_REF = MethodReference.findOrCreate(TypeReference.JavaLangClass,
      newInstanceAtom, classNewInstanceDescriptor);

  private final static Atom defCtorAtom = Atom.findOrCreateUnicodeAtom("");

  private final static Descriptor defCtorDescriptor = Descriptor.findOrCreateUTF8("()V");

  private final static Selector defCtorSelector = new Selector(defCtorAtom, defCtorDescriptor);

  private final IClassHierarchy cha;

/** BEGIN Custom change: caching */
  private final Map cache = HashMapFactory.make();
  
/** END Custom change: caching */
  public ClassNewInstanceContextInterpreter(IClassHierarchy cha) {
    this.cha = cha;
  }

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
/** BEGIN Custom change: caching */
    
    final JavaTypeContext context = (JavaTypeContext) node.getContext();
    final IMethod method = node.getMethod();
    final String hashKey = method.toString() + "@" + context.toString();
    
    IR result = cache.get(hashKey);
    
    if (result == null) {
      result = makeIR(method, context);
      cache.put(hashKey, result);
    }
    
/** END Custom change: caching */
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    if (!(node.getContext() instanceof JavaTypeContext)) {
      return false;
    }
    return node.getMethod().getReference().equals(CLASS_NEW_INSTANCE_REF);
  }

  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    JavaTypeContext context = (JavaTypeContext) node.getContext();
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      return new NonNullSingletonIterator(NewSiteReference.make(0, tr));
    }
    return EmptyIterator.instance();
  }

  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return EmptyIterator.instance();
  }

  private IR makeIR(IMethod method, JavaTypeContext context) {
    SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory();
    TypeReference tr = context.getType().getTypeReference();
    if (tr != null) {
      SpecializedMethod m = new SpecializedMethod(method, method.getDeclaringClass(), method.isStatic(), false);
      IClass klass = cha.lookupClass(tr);
      IMethod publicDefaultCtor = getPublicDefaultCtor(klass);
      if (publicDefaultCtor != null) {
        m.addStatementsForConcreteSimpleType(tr);
      } else if (klass.getMethod(defCtorSelector) == null) {
        TypeReference instantiationExceptionRef = TypeReference.findOrCreateClass(ClassLoaderReference.Primordial, "java/lang",
            "InstantiationException");
        int xobj = method.getNumberOfParameters() + 1;
        SSAInstruction newStatement = insts.NewInstruction(m.allInstructions.size(), xobj, NewSiteReference.make(2, instantiationExceptionRef));
        m.addInstruction(tr, newStatement, true);
        SSAInstruction throwStatement = insts.ThrowInstruction(m.allInstructions.size(), xobj);
        m.addInstruction(tr, throwStatement, false);
      } else {
        TypeReference illegalAccessExceptionRef = TypeReference.findOrCreateClass(ClassLoaderReference.Primordial, "java/lang",
            "IllegalAccessException");
        int xobj = method.getNumberOfParameters() + 1;
        SSAInstruction newStatement = insts.NewInstruction(m.allInstructions.size(), xobj, NewSiteReference.make(2, illegalAccessExceptionRef));
        m.addInstruction(tr, newStatement, true);
        SSAInstruction throwStatement = insts.ThrowInstruction(m.allInstructions.size(), xobj);
        m.addInstruction(tr, throwStatement, false);
      }

      SSAInstruction[] instrs = new SSAInstruction[m.allInstructions.size()];
      m.allInstructions. toArray(instrs);
      return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
    }

    return null;
  }

  private IMethod getPublicDefaultCtor(IClass klass) {
    IMethod ctorMethod = klass.getMethod(defCtorSelector);
    if (ctorMethod != null && ctorMethod.isPublic()) {
      return ctorMethod;
    }
    return null;
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
File
ClassNewInstanceContextInterpreter.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
  /**
  /**
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.CodeScanner;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.ContextUtil;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.strings.Atom;

/**
 * A context interpreter for java.lang.Object.clone
 * 
 * TODO: The current implementation does not model CloneNotSupportedExceptions
 */
public class CloneInterpreter implements SSAContextInterpreter {

  /**
   * Comment for cloneAtom
   */
  public final static Atom cloneAtom = Atom.findOrCreateUnicodeAtom("clone");

  private final static Descriptor cloneDesc = Descriptor.findOrCreateUTF8("()Ljava/lang/Object;");
   * Comment for CLONE
   */
  public final static MethodReference CLONE = MethodReference.findOrCreate(TypeReference.JavaLangObject, cloneAtom, cloneDesc);

  private final static TypeReference SYNTHETIC_SYSTEM = TypeReference.findOrCreate(ClassLoaderReference.Primordial, TypeName
      .string2TypeName("Lcom/ibm/wala/model/java/lang/System"));

  private final static Atom arraycopyAtom = Atom.findOrCreateUnicodeAtom("arraycopy");

  private final static Descriptor arraycopyDesc = Descriptor.findOrCreateUTF8("(Ljava/lang/Object;Ljava/lang/Object;)V");

  private final static MethodReference SYNTHETIC_ARRAYCOPY = MethodReference.findOrCreate(SYNTHETIC_SYSTEM, arraycopyAtom,
      arraycopyDesc);

  /**
   * If the type is an array, the program counter of the synthesized call to arraycopy. Doesn't really matter what it is.
   */
  private final static int ARRAYCOPY_PC = 3;

  private final static CallSiteReference ARRAYCOPY_SITE = CallSiteReference.make(ARRAYCOPY_PC, SYNTHETIC_ARRAYCOPY,
      IInvokeInstruction.Dispatch.STATIC);

  private final static int NEW_PC = 0;

  /**
   * Mapping from TypeReference -> IR TODO: Soft references?
   */
  final private Map IRCache = HashMapFactory.make();

  private final SSAInstructionFactory insts = Language.JAVA.instructionFactory();

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    IClass cls = ContextUtil.getConcreteClassFromContext(node.getContext());

    IR result = IRCache.get(cls.getReference());
    if (result == null) {
      result = makeIR(node.getMethod(), node.getContext(), cls);
      IRCache.put(cls.getReference(), result);
    }
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    return (node.getMethod().getReference().equals(CLONE) && ContextUtil.getConcreteClassFromContext(node.getContext()) != null);
  }

  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    IClass cls = ContextUtil.getConcreteClassFromContext(node.getContext());
    return new NonNullSingletonIterator(NewSiteReference.make(NEW_PC, cls.getReference()));
  }

  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return new NonNullSingletonIterator(ARRAYCOPY_SITE);
  }

  /**
   * @return an array of statements that encode the behavior of the clone method for a given type.
   */
  private SSAInstruction[] makeStatements(IClass klass) {
    assert klass != null;

    ArrayList statements = new ArrayList();
    // value number 1 is "this".
    int nextLocal = 2;

    int retValue = nextLocal++;
    // value number of the result of the clone()
    NewSiteReference ref = NewSiteReference.make(NEW_PC, klass.getReference());
    SSANewInstruction N = null;
    if (klass.isArrayClass()) {
      int length = nextLocal++;
      statements.add(insts.ArrayLengthInstruction(statements.size(), length, 1));
      int[] sizes = new int[klass.getReference().getDimensionality()];
      Arrays.fill(sizes, length);
      N = insts.NewInstruction(statements.size(), retValue, ref, sizes);
    } else {

      N = insts.NewInstruction(statements.size(), retValue, ref);
    }
    statements.add(N);

    int exceptionValue = nextLocal++;

    if (klass.getReference().isArrayType()) {
      // generate a synthetic arraycopy from this (v.n. 1) to the clone
      int[] params = new int[2];
      params[0] = 1;
      params[1] = retValue;
      SSAInvokeInstruction S = insts.InvokeInstruction(statements.size(), params, exceptionValue, ARRAYCOPY_SITE);
      statements.add(S);
    } else {
      // copy the fields over, one by one.
      // TODO:
      IClass k = klass;
      while (k != null) {
        for (Iterator it = klass.getDeclaredInstanceFields().iterator(); it.hasNext();) {
          IField f = it.next();
          int tempValue = nextLocal++;
          SSAGetInstruction G = insts.GetInstruction(statements.size(), tempValue, 1, f.getReference());
          statements.add(G);
          SSAPutInstruction P = insts.PutInstruction(statements.size(), retValue, tempValue, f.getReference());
          statements.add(P);
        }
        k = k.getSuperclass();
      }

    }

    SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), retValue, false);
    statements.add(R);

    SSAInstruction[] result = new SSAInstruction[statements.size()];
    Iterator it = statements.iterator();
    for (int i = 0; i < result.length; i++) {
      result[i] = it.next();
    }
    return result;
  }

   * @return an IR that encodes the behavior of the clone method for a given type.
   */
  private IR makeIR(IMethod method, Context context, IClass klass) {
    assert klass != null;
    SSAInstruction instrs[] = makeStatements(klass);
    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.cfa.CFAContextInterpreter#recordFactoryType(com.ibm.wala.ipa.callgraph.CGNode,
   * com.ibm.wala.classLoader.IClass)
   */
  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.getFieldsRead(statements).iterator();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.getFieldsWritten(statements).iterator();
  }

  public Set getCaughtExceptions(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.getCaughtExceptions(node.getMethod().getDeclaringClass().getClassLoader().getLanguage(), statements);
  }

  public boolean hasObjectArrayLoad(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.hasObjectArrayLoad(statements);
  }

  public boolean hasObjectArrayStore(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.hasObjectArrayStore(statements);
  }

  public Iterator iterateCastTypes(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.iterateCastTypes(statements);
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.CodeScanner;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.ContextUtil;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.strings.Atom;

/**
 * A context interpreter for java.lang.Object.clone
 * 
 * TODO: The current implementation does not model CloneNotSupportedExceptions
 */
public class CloneInterpreter implements SSAContextInterpreter {

  /**
   * Comment for cloneAtom
   */
  public final static Atom cloneAtom = Atom.findOrCreateUnicodeAtom("clone");

  private final static Descriptor cloneDesc = Descriptor.findOrCreateUTF8("()Ljava/lang/Object;");

  /**
   * Comment for CLONE
   */
  public final static MethodReference CLONE = MethodReference.findOrCreate(TypeReference.JavaLangObject, cloneAtom, cloneDesc);

  private final static TypeReference SYNTHETIC_SYSTEM = TypeReference.findOrCreate(ClassLoaderReference.Primordial, TypeName
      .string2TypeName("Lcom/ibm/wala/model/java/lang/System"));

  private final static Atom arraycopyAtom = Atom.findOrCreateUnicodeAtom("arraycopy");

  private final static Descriptor arraycopyDesc = Descriptor.findOrCreateUTF8("(Ljava/lang/Object;Ljava/lang/Object;)V");

  private final static MethodReference SYNTHETIC_ARRAYCOPY = MethodReference.findOrCreate(SYNTHETIC_SYSTEM, arraycopyAtom,
      arraycopyDesc);

  /**
   * If the type is an array, the program counter of the synthesized call to arraycopy. Doesn't really matter what it is.
   */
  private final static int ARRAYCOPY_PC = 3;

  private final static CallSiteReference ARRAYCOPY_SITE = CallSiteReference.make(ARRAYCOPY_PC, SYNTHETIC_ARRAYCOPY,
      IInvokeInstruction.Dispatch.STATIC);
  private final static int NEW_PC = 0;

  /**
   * Mapping from TypeReference -> IR TODO: Soft references?
   */
  final private Map IRCache = HashMapFactory.make();

  private final SSAInstructionFactory insts = Language.JAVA.instructionFactory();

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    IClass cls = ContextUtil.getConcreteClassFromContext(node.getContext());
    IR result = IRCache.get(cls.getReference());
    if (result == null) {
      result = makeIR(node.getMethod(), node.getContext(), cls);
      IRCache.put(cls.getReference(), result);
    }
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    return (node.getMethod().getReference().equals(CLONE) && ContextUtil.getConcreteClassFromContext(node.getContext()) != null);
  }

  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    IClass cls = ContextUtil.getConcreteClassFromContext(node.getContext());
    return new NonNullSingletonIterator(NewSiteReference.make(NEW_PC, cls.getReference()));
  }

  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return new NonNullSingletonIterator(ARRAYCOPY_SITE);
  }

  /**
   * @return an array of statements that encode the behavior of the clone method for a given type.
   */
  private SSAInstruction[] makeStatements(IClass klass) {
    assert klass != null;

    ArrayList statements = new ArrayList();
    // value number 1 is "this".
    int nextLocal = 2;

    int retValue = nextLocal++;
    // value number of the result of the clone()
    NewSiteReference ref = NewSiteReference.make(NEW_PC, klass.getReference());
    SSANewInstruction N = null;
    if (klass.isArrayClass()) {
      int length = nextLocal++;
      statements.add(insts.ArrayLengthInstruction(length, 1));
      int[] sizes = new int[((ArrayClass)klass).getDimensionality()];
      Arrays.fill(sizes, length);
      N = insts.NewInstruction(retValue, ref, sizes);
    } else {
      N = insts.NewInstruction(retValue, ref);
    }
    statements.add(N);

    int exceptionValue = nextLocal++;

    if (klass.getReference().isArrayType()) {
      // generate a synthetic arraycopy from this (v.n. 1) to the clone
      int[] params = new int[2];
      params[0] = 1;
      params[1] = retValue;
      SSAInvokeInstruction S = insts.InvokeInstruction(params, exceptionValue, ARRAYCOPY_SITE);
      statements.add(S);
    } else {
      // copy the fields over, one by one.
      // TODO:
      IClass k = klass;
      while (k != null) {
        for (Iterator it = klass.getDeclaredInstanceFields().iterator(); it.hasNext();) {
          IField f = it.next();
          int tempValue = nextLocal++;
          SSAGetInstruction G = insts.GetInstruction(tempValue, 1, f.getReference());
          statements.add(G);
          SSAPutInstruction P = insts.PutInstruction(retValue, tempValue, f.getReference());
          statements.add(P);
        }
        k = k.getSuperclass();
      }

    }

    SSAReturnInstruction R = insts.ReturnInstruction(retValue, false);
    statements.add(R);

    SSAInstruction[] result = new SSAInstruction[statements.size()];
    Iterator it = statements.iterator();
    for (int i = 0; i < result.length; i++) {
      result[i] = it.next();
    }
    return result;
  }

  /**
   * @return an IR that encodes the behavior of the clone method for a given type.
   */
  private IR makeIR(IMethod method, Context context, IClass klass) {
    assert klass != null;
    SSAInstruction instrs[] = makeStatements(klass);
    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.cfa.CFAContextInterpreter#recordFactoryType(com.ibm.wala.ipa.callgraph.CGNode,
   * com.ibm.wala.classLoader.IClass)
   */
  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.getFieldsRead(statements).iterator();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.getFieldsWritten(statements).iterator();
  }
  public Set getCaughtExceptions(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.getCaughtExceptions(node.getMethod().getDeclaringClass().getClassLoader().getLanguage(), statements);
  }

  public boolean hasObjectArrayLoad(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.hasObjectArrayLoad(statements);
  }

  public boolean hasObjectArrayStore(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.hasObjectArrayStore(statements);
  }

  public Iterator iterateCastTypes(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.iterateCastTypes(statements);
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
   */
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.CodeScanner;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.ContextUtil;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.strings.Atom;

/**
 * A context interpreter for java.lang.Object.clone
 * 
 * TODO: The current implementation does not model CloneNotSupportedExceptions
 */
public class CloneInterpreter implements SSAContextInterpreter {

  /**
   * Comment for cloneAtom
   */
  public final static Atom cloneAtom = Atom.findOrCreateUnicodeAtom("clone");

  private final static Descriptor cloneDesc = Descriptor.findOrCreateUTF8("()Ljava/lang/Object;");

  /**
   * Comment for CLONE
   */
  public final static MethodReference CLONE = MethodReference.findOrCreate(TypeReference.JavaLangObject, cloneAtom, cloneDesc);

  private final static TypeReference SYNTHETIC_SYSTEM = TypeReference.findOrCreate(ClassLoaderReference.Primordial, TypeName
      .string2TypeName("Lcom/ibm/wala/model/java/lang/System"));

  private final static Atom arraycopyAtom = Atom.findOrCreateUnicodeAtom("arraycopy");

  private final static Descriptor arraycopyDesc = Descriptor.findOrCreateUTF8("(Ljava/lang/Object;Ljava/lang/Object;)V");

  private final static MethodReference SYNTHETIC_ARRAYCOPY = MethodReference.findOrCreate(SYNTHETIC_SYSTEM, arraycopyAtom,
      arraycopyDesc);

  /**
   * If the type is an array, the program counter of the synthesized call to arraycopy. Doesn't really matter what it is.
   */
  private final static int ARRAYCOPY_PC = 3;

  private final static CallSiteReference ARRAYCOPY_SITE = CallSiteReference.make(ARRAYCOPY_PC, SYNTHETIC_ARRAYCOPY,
      IInvokeInstruction.Dispatch.STATIC);

  private final static int NEW_PC = 0;

  /**
   * Mapping from TypeReference -> IR TODO: Soft references?
  final private Map IRCache = HashMapFactory.make();

  private final SSAInstructionFactory insts = Language.JAVA.instructionFactory();

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    IClass cls = ContextUtil.getConcreteClassFromContext(node.getContext());
    IR result = IRCache.get(cls.getReference());
    if (result == null) {
   */
      result = makeIR(node.getMethod(), node.getContext(), cls);
      IRCache.put(cls.getReference(), result);
    }
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    return (node.getMethod().getReference().equals(CLONE) && ContextUtil.getConcreteClassFromContext(node.getContext()) != null);
  }

  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    IClass cls = ContextUtil.getConcreteClassFromContext(node.getContext());
    return new NonNullSingletonIterator(NewSiteReference.make(NEW_PC, cls.getReference()));
  }

  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return new NonNullSingletonIterator(ARRAYCOPY_SITE);
  }

  /**
   * @return an array of statements that encode the behavior of the clone method for a given type.
   */
  private SSAInstruction[] makeStatements(IClass klass) {
    assert klass != null;

    ArrayList statements = new ArrayList();
    // value number 1 is "this".
    int nextLocal = 2;

    int retValue = nextLocal++;
    // value number of the result of the clone()
    NewSiteReference ref = NewSiteReference.make(NEW_PC, klass.getReference());
    SSANewInstruction N = null;
    if (klass.isArrayClass()) {
      int length = nextLocal++;
      statements.add(insts.ArrayLengthInstruction(statements.size(), length, 1));
      int[] sizes = new int[((ArrayClass)klass).getDimensionality()];
      Arrays.fill(sizes, length);
      N = insts.NewInstruction(statements.size(), retValue, ref, sizes);
    } else {
      N = insts.NewInstruction(statements.size(), retValue, ref);
    }
    statements.add(N);

    int exceptionValue = nextLocal++;

    if (klass.getReference().isArrayType()) {
      // generate a synthetic arraycopy from this (v.n. 1) to the clone
      int[] params = new int[2];
      params[0] = 1;
      params[1] = retValue;
      SSAInvokeInstruction S = insts.InvokeInstruction(statements.size(), params, exceptionValue, ARRAYCOPY_SITE);
      statements.add(S);
    } else {
      // copy the fields over, one by one.
      // TODO:
      IClass k = klass;
      while (k != null) {
        for (Iterator it = klass.getDeclaredInstanceFields().iterator(); it.hasNext();) {
          IField f = it.next();
          int tempValue = nextLocal++;
          SSAGetInstruction G = insts.GetInstruction(statements.size(), tempValue, 1, f.getReference());
          statements.add(G);
          SSAPutInstruction P = insts.PutInstruction(statements.size(), retValue, tempValue, f.getReference());
          statements.add(P);
        }
        k = k.getSuperclass();
      }

    }

    SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), retValue, false);
    statements.add(R);

    SSAInstruction[] result = new SSAInstruction[statements.size()];
    Iterator it = statements.iterator();
    for (int i = 0; i < result.length; i++) {
      result[i] = it.next();
    }
    return result;
  }

  /**
   * @return an IR that encodes the behavior of the clone method for a given type.
  private IR makeIR(IMethod method, Context context, IClass klass) {
    assert klass != null;
    SSAInstruction instrs[] = makeStatements(klass);
    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.cfa.CFAContextInterpreter#recordFactoryType(com.ibm.wala.ipa.callgraph.CGNode,
   * com.ibm.wala.classLoader.IClass)
   */
  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.getFieldsRead(statements).iterator();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.getFieldsWritten(statements).iterator();
  }

  public Set getCaughtExceptions(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.getCaughtExceptions(node.getMethod().getDeclaringClass().getClassLoader().getLanguage(), statements);
  }

  public boolean hasObjectArrayLoad(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.hasObjectArrayLoad(statements);
  }

  public boolean hasObjectArrayStore(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.hasObjectArrayStore(statements);
  }

  public Iterator iterateCastTypes(CGNode node) {
    SSAInstruction[] statements = getIR(node).getInstructions();
    return CodeScanner.iterateCastTypes(statements);
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
File
CloneInterpreter.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
  }
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.analysis.typeInference.ConeType;
import com.ibm.wala.analysis.typeInference.PointType;
import com.ibm.wala.analysis.typeInference.SetType;
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.CodeScanner;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.SummarizedMethod;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.warnings.Warnings;

/**
 * Logic to interpret "factory" methods in context.
 */

class FactoryBypassInterpreter extends AbstractReflectionInterpreter {

  /**
   * A Map from CallerSiteContext -> Set represents the types a factory method might create in a particular context
   */
  private final Map> map = HashMapFactory.make();

  /**
   * A cache of synthetic method implementations, indexed by Context
   */
  private final Map syntheticMethodCache = HashMapFactory.make();

  /**
   * @param options governing analysis options
   */
  public FactoryBypassInterpreter(AnalysisOptions options, AnalysisCache cache) {
    this.options = options;
    this.cache = cache;
  }

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return cache.getSSACache().findOrCreateIR(m, node.getContext(), options.getSSAOptions());
  }

  private Set getTypesForContext(Context context) {
    // first try user spec
    // XMLReflectionReader spec = (XMLReflectionReader) userSpec;
    // if (spec != null && context instanceof CallerSiteContext) {
    // CallerSiteContext site = (CallerSiteContext) context;
    // MemberReference m = site.getCaller().getMethod().getReference();
    // ReflectionSummary summary = spec.getSummary(m);
    // if (summary != null) {
    // Set types = summary.getTypesForProgramLocation(site.getCallSite().getProgramCounter());
    // if (types != null) {
    // return types;
    // }
    // }
    // }

    Set types = map.get(context);
    return types;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getNumberOfStatements(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public int getNumberOfStatements(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return m.allInstructions.size();
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.rta.RTAContextInterpreter#understands(com.ibm.wala.classLoader.IMethod,
   * com.ibm.wala.ipa.callgraph.Context)
   */
  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (node.getMethod().isSynthetic()) {
      SyntheticMethod s = (SyntheticMethod) node.getMethod();
      if (s.isFactoryMethod()) {
        return getTypesForContext(node.getContext()) != null;
      }
    }
    return false;

  }

  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    HashSet result = HashSetFactory.make(5);
    for (Iterator it = m.getAllocationStatements().iterator(); it.hasNext();) {
      SSANewInstruction s = (SSANewInstruction) it.next();
      result.add(s.getNewSite());
    }
    return result.iterator();
  }

  public Iterator getInvokeStatements(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return m.getInvokeStatements().iterator();
  public Iterator iterateCallSites(CGNode node) {
    final Iterator I = getInvokeStatements(node);
    return new Iterator() {
      public boolean hasNext() {
        return I.hasNext();
      }

      public CallSiteReference next() {
        SSAInvokeInstruction s = (SSAInvokeInstruction) I.next();
        return s.getCallSite();
      }

      public void remove() {
        Assertions.UNREACHABLE();
      }
    };
  }

  public boolean recordType(IClassHierarchy cha, Context context, TypeReference type) {
    Set types = map.get(context);
    if (types == null) {
      types = HashSetFactory.make(2);
      map.put(context, types);
    }
    if (types.contains(type)) {
      return false;
    } else {
      types.add(type);
      // update any extant synthetic method
      SpecializedFactoryMethod m = syntheticMethodCache.get(context);
      if (m != null) {
        TypeAbstraction T = typeRef2TypeAbstraction(cha, type);
        m.addStatementsForTypeAbstraction(T);
        cache.getSSACache().invalidate(m, context);
      }
      return true;
    }
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#recordFactoryType(com.ibm.wala.ipa.callgraph.CGNode,
   * com.ibm.wala.classLoader.IClass)
   */
  public boolean recordFactoryType(CGNode node, IClass klass) {
    if (klass == null) {
      throw new IllegalArgumentException("klass is null");
    }
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    return recordType(node.getMethod().getClassHierarchy(), node.getContext(), klass.getReference());
  }

  public Iterator iterateFieldsRead(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.getFieldsRead(m).iterator();
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.getFieldsWritten(m).iterator();
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  private SpecializedFactoryMethod findOrCreateSpecializedFactoryMethod(CGNode node) {
    SpecializedFactoryMethod m = syntheticMethodCache.get(node.getContext());
    if (m == null) {
      Set types = getTypesForContext(node.getContext());
      m = new SpecializedFactoryMethod((SummarizedMethod) node.getMethod(), node.getContext(), types);
      syntheticMethodCache.put(node.getContext(), m);
    }
    return m;
  }

  public Set getCaughtExceptions(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.getCaughtExceptions(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  public boolean hasObjectArrayLoad(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.hasObjectArrayLoad(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return false;
    }
  }

  public boolean hasObjectArrayStore(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.hasObjectArrayStore(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return false;
    }
  }

  public Iterator iterateCastTypes(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.iterateCastTypes(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getCFG(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return cache.getSSACache().findOrCreateDU(m, node.getContext(), options.getSSAOptions());
  }

  protected class SpecializedFactoryMethod extends SpecializedMethod {

    /**
     * List of synthetic invoke instructions we model for this specialized instance.
     */
    final private ArrayList calls = new ArrayList();

    /**
     * The method being modelled
     */
    private final IMethod method;

    /**
     * Context being modelled
     */
    private final Context context;

    /**
     * next free local value number;
     */
    private int nextLocal;

    /**
     * value number for integer constant 1
     */
    private int valueNumberForConstantOne = -1;

    private final SSAInstructionFactory insts = declaringClass.getClassLoader().getInstructionFactory();

    private void initValueNumberForConstantOne() {
      if (valueNumberForConstantOne == -1) {
        valueNumberForConstantOne = nextLocal++;
      }
    }

    protected SpecializedFactoryMethod(final SummarizedMethod m, Context context, final Set S) {
      super(m, m.getDeclaringClass(), m.isStatic(), true);

      this.context = context;
      if (DEBUG) {
        System.err.println(("Create SpecializedFactoryMethod " + m + S));
      }

      this.method = m;
      assert S != null;
     * @param T
      assert m.getDeclaringClass() != null : "null declaring class for " + m;

      // add original statements from the method summary
      nextLocal = addOriginalStatements(m);

      for (Iterator it = S.iterator(); it.hasNext();) {
        TypeReference type = (TypeReference) it.next();
        TypeAbstraction T = typeRef2TypeAbstraction(m.getClassHierarchy(), type);
        addStatementsForTypeAbstraction(T);
      }
    }

    protected void addStatementsForTypeAbstraction(TypeAbstraction T) {

      if (DEBUG) {
        System.err.println(("adding " + T + " to " + method));
      }
      T = interceptType(T);
      if (T == null) {
        return;
      }
      if ((T instanceof PointType) || (T instanceof ConeType)) {
        TypeReference ref = T.getType().getReference();
        NewSiteReference site = NewSiteReference.make(0, ref);

        if (DEBUG) {
          IClass klass = options.getClassTargetSelector().getAllocatedTarget(null, site);
          System.err.println(("Selected allocated target: " + klass + " for " + T));
        }
        if (T instanceof PointType) {
          if (!typesAllocated.contains(ref)) {
            addStatementsForConcreteType(ref);
          }
        } else if (T instanceof ConeType) {
          if (DEBUG) {
            System.err.println(("Cone clause for " + T));
          }
          if (((ConeType) T).isInterface()) {
            Set implementors = T.getType().getClassHierarchy().getImplementors(ref);
            if (DEBUG) {
              System.err.println(("Implementors for " + T + " " + implementors));
            }
            if (implementors.isEmpty()) {
              if (DEBUG) {
                System.err.println(("Found no implementors of type " + T));
              }
              Warnings.add(NoSubtypesWarning.create(T));
            }
            if (implementors.size() > CONE_BOUND) {
              Warnings.add(ManySubtypesWarning.create(T, implementors.size()));
            }

            addStatementsForSetOfTypes(implementors.iterator());
          } else {
        TypeReference T = klass.getReference();
            Collection subclasses = T.getType().getClassHierarchy().computeSubClasses(ref);
            if (DEBUG) {
              System.err.println(("Subclasses for " + T + " " + subclasses));
            }
            if (subclasses.isEmpty()) {
              if (DEBUG) {
                System.err.println(("Found no subclasses of type " + T));
              }
              Warnings.add(NoSubtypesWarning.create(T));
            }
            if (subclasses.size() > CONE_BOUND) {
              Warnings.add(ManySubtypesWarning.create(T, subclasses.size()));
            }
            addStatementsForSetOfTypes(subclasses.iterator());
          }
        } else {
          Assertions.UNREACHABLE("Unexpected type " + T.getClass());
        }
      } else if (T instanceof SetType) {
        // This code has clearly bitrotted, since iteratePoints() returns an Iterator
        // and we need an Iterator. Commenting out for now. --MS
        Assertions.UNREACHABLE();
        // addStatementsForSetOfTypes(((SetType) T).iteratePoints());
      } else {
        Assertions.UNREACHABLE("Unexpected type " + T.getClass());
      }
    }

    private TypeAbstraction interceptType(TypeAbstraction T) {
      TypeReference type = T.getType().getReference();
      if (type.equals(TypeReference.JavaIoSerializable)) {
        Warnings.add(IgnoreSerializableWarning.create());
        return null;
      } else {
        return T;
      }
    }

    /**
     * Set up a method summary which allocates and returns an instance of concrete type T.
     * 
     */
    private void addStatementsForConcreteType(final TypeReference T) {
      int alloc = addStatementsForConcreteSimpleType(T);
      if (alloc == -1) {
        return;
      }
      if (T.isArrayType()) {
        MethodReference init = MethodReference.findOrCreate(T, MethodReference.initAtom, MethodReference.defaultInitDesc);
        CallSiteReference site = CallSiteReference.make(getCallSiteForType(T), init, IInvokeInstruction.Dispatch.SPECIAL);
        int[] params = new int[1];
        params[0] = alloc;
        int exc = getExceptionsForType(T);
        SSAInvokeInstruction s = insts.InvokeInstruction(allInstructions.size(), params, exc, site);
        calls.add(s);
        allInstructions.add(s);
      }
    }

    private int addOriginalStatements(SummarizedMethod m) {
      SSAInstruction[] original = m.getStatements(options.getSSAOptions());
      // local value number 1 is "this", so the next free value number is 2
      int nextLocal = 2;
      for (int i = 0; i < original.length; i++) {
        SSAInstruction s = original[i];
        allInstructions.add(s);
        if (s instanceof SSAInvokeInstruction) {
          calls.add(s);
        }
        if (s instanceof SSANewInstruction) {
          allocations.add(s);
        }
        for (int j = 0; j < s.getNumberOfDefs(); j++) {
          int def = s.getDef(j);
          if (def >= nextLocal) {
            nextLocal = def + 1;
          }
        }
        for (int j = 0; j < s.getNumberOfUses(); j++) {
          int use = s.getUse(j);
          if (use >= nextLocal) {
            nextLocal = use + 1;
          }
        }
      }
      return nextLocal;
    }

    private void addStatementsForSetOfTypes(Iterator it) {
      if (!it.hasNext()) { // Uh. No types. Hope the caller reported a warning.
        SSAReturnInstruction r = insts.ReturnInstruction(allInstructions.size(), nextLocal, false);
        allInstructions.add(r);
      }

      for (; it.hasNext();) {
        IClass klass = it.next();
        if (klass.isAbstract() || klass.isInterface() || typesAllocated.contains(T)) {
          continue;
        }
        typesAllocated.add(T);
        int i = getLocalForType(T);
        NewSiteReference ref = NewSiteReference.make(getNewSiteForType(T), T);
        SSANewInstruction a = null;
        if (T.isArrayType()) {
          int[] sizes = new int[T.getDimensionality()];
          initValueNumberForConstantOne();
          Arrays.fill(sizes, valueNumberForConstantOne);
          a = insts.NewInstruction(allInstructions.size(), i, ref, sizes);

        } else {
          a = insts.NewInstruction(allInstructions.size(), i, ref);
        }
        allocations.add(a);
        allInstructions.add(a);
        SSAReturnInstruction r = insts.ReturnInstruction(allInstructions.size(), i, false);
        allInstructions.add(r);
        MethodReference init = MethodReference.findOrCreate(T, MethodReference.initAtom, MethodReference.defaultInitDesc);
        CallSiteReference site = CallSiteReference.make(getCallSiteForType(T), init, IInvokeInstruction.Dispatch.SPECIAL);
        int[] params = new int[1];
        params[0] = i;
        SSAInvokeInstruction s = insts.InvokeInstruction(allInstructions.size(), params, getExceptionsForType(T), site);
        calls.add(s);
        allInstructions.add(s);
      }
    }

    public List getAllocationStatements() {
      return allocations;
    }

    public List getInvokeStatements() {
      return calls;
    }

    /**
     * Two specialized methods can be different, even if they represent the same source method. So, revert to object identity for
     * testing equality. TODO: this is non-optimal; could try to re-use specialized methods that have the same context.
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
      return this == obj;
    }

    @Override
    public int hashCode() { // TODO: change this to avoid non-determinism!
      return System.identityHashCode(this);
    }

    @Override
    public String toString() {
      return super.toString();
    }

    @Override
    public SSAInstruction[] getStatements() {
      SSAInstruction[] result = new SSAInstruction[allInstructions.size()];
      int i = 0;
      for (Iterator it = allInstructions.iterator(); it.hasNext();) {
        result[i++] = it.next();
      }
      return result;
    }

    @Override
    public IClass getDeclaringClass() {
      assert method.getDeclaringClass() != null : "null declaring class for original method " + method;
      return method.getDeclaringClass();
    }

    @Override
    public int getNumberOfParameters() {
      return method.getNumberOfParameters();
    }

    @Override
    public TypeReference getParameterType(int i) {
      return method.getParameterType(i);
    }

    /*
     * @see com.ibm.wala.classLoader.IMethod#getIR(com.ibm.wala.util.WarningSet)
     */
    @Override
    public IR makeIR(Context C, SSAOptions options) {
      SSAInstruction[] instrs = getStatements();
      Map constants = null;
      if (valueNumberForConstantOne > -1) {
        constants = HashMapFactory.make(1);
        constants.put(new Integer(valueNumberForConstantOne), new ConstantValue(new Integer(1)));
      }

      System.err.println("generating IR for " + node);
    }
      return new SyntheticIR(this, context, new InducedCFG(instrs, this, context), instrs, options, constants);
    }
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.analysis.typeInference.ConeType;
import com.ibm.wala.analysis.typeInference.PointType;
import com.ibm.wala.analysis.typeInference.SetType;
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.CodeScanner;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.SummarizedMethod;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.warnings.Warnings;

/**
 * Logic to interpret "factory" methods in context.
 */
class FactoryBypassInterpreter extends AbstractReflectionInterpreter {

  /**
   * A Map from CallerSiteContext -> Set represents the types a factory method might create in a particular context
   */
  private final Map> map = HashMapFactory.make();

  /**
   * A cache of synthetic method implementations, indexed by Context
   */
  private final Map syntheticMethodCache = HashMapFactory.make();

  /**
   * @param options governing analysis options
   */
  public FactoryBypassInterpreter(AnalysisOptions options, AnalysisCache cache) {
    this.options = options;
    this.cache = cache;
  }

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (DEBUG) {
    } else {
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return cache.getSSACache().findOrCreateIR(m, node.getContext(), options.getSSAOptions());
  }

  private Set getTypesForContext(Context context) {
    // first try user spec
    // XMLReflectionReader spec = (XMLReflectionReader) userSpec;
    // if (spec != null && context instanceof CallerSiteContext) {
    // CallerSiteContext site = (CallerSiteContext) context;
    // MemberReference m = site.getCaller().getMethod().getReference();
    // ReflectionSummary summary = spec.getSummary(m);
    // if (summary != null) {
    // Set types = summary.getTypesForProgramLocation(site.getCallSite().getProgramCounter());
    // if (types != null) {
    // return types;
    // }
    // }
    // }

    Set types = map.get(context);
    return types;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getNumberOfStatements(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public int getNumberOfStatements(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return m.allInstructions.size();
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.rta.RTAContextInterpreter#understands(com.ibm.wala.classLoader.IMethod,
   * com.ibm.wala.ipa.callgraph.Context)
   */
  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (node.getMethod().isSynthetic()) {
      SyntheticMethod s = (SyntheticMethod) node.getMethod();
      if (s.isFactoryMethod()) {
        return getTypesForContext(node.getContext()) != null;
      }
    }
    return false;

  }

  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    HashSet result = HashSetFactory.make(5);
    for (Iterator it = m.getAllocationStatements().iterator(); it.hasNext();) {
      SSANewInstruction s = (SSANewInstruction) it.next();
      result.add(s.getNewSite());
    }
    return result.iterator();
  }

  public Iterator getInvokeStatements(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return m.getInvokeStatements().iterator();
  }

  public Iterator iterateCallSites(CGNode node) {
    final Iterator I = getInvokeStatements(node);
    return new Iterator() {
      public boolean hasNext() {
        return I.hasNext();
      }

      public CallSiteReference next() {
        SSAInvokeInstruction s = (SSAInvokeInstruction) I.next();
        return s.getCallSite();
      }

      public void remove() {
        Assertions.UNREACHABLE();
      }
    };
  }

  public boolean recordType(IClassHierarchy cha, Context context, TypeReference type) {
    Set types = map.get(context);
    if (types == null) {
      types = HashSetFactory.make(2);
      map.put(context, types);
    }
    if (types.contains(type)) {
      return false;
      types.add(type);
      // update any extant synthetic method
      SpecializedFactoryMethod m = syntheticMethodCache.get(context);
      if (m != null) {
        TypeAbstraction T = typeRef2TypeAbstraction(cha, type);
        m.addStatementsForTypeAbstraction(T);
        cache.getSSACache().invalidate(m, context);
      }
      return true;
    }
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#recordFactoryType(com.ibm.wala.ipa.callgraph.CGNode,
   * com.ibm.wala.classLoader.IClass)
   */
  public boolean recordFactoryType(CGNode node, IClass klass) {
    if (klass == null) {
      throw new IllegalArgumentException("klass is null");
    }
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    return recordType(node.getMethod().getClassHierarchy(), node.getContext(), klass.getReference());
  }

  public Iterator iterateFieldsRead(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.getFieldsRead(m).iterator();
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.getFieldsWritten(m).iterator();
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  private SpecializedFactoryMethod findOrCreateSpecializedFactoryMethod(CGNode node) {
    SpecializedFactoryMethod m = syntheticMethodCache.get(node.getContext());
    if (m == null) {
      Set types = getTypesForContext(node.getContext());
      m = new SpecializedFactoryMethod((SummarizedMethod) node.getMethod(), node.getContext(), types);
      syntheticMethodCache.put(node.getContext(), m);
    }
    return m;
  }

  public Set getCaughtExceptions(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.getCaughtExceptions(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  public boolean hasObjectArrayLoad(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.hasObjectArrayLoad(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return false;
    }
  }

  public boolean hasObjectArrayStore(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.hasObjectArrayStore(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return false;
    }
  }

  public Iterator iterateCastTypes(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.iterateCastTypes(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getCFG(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return cache.getSSACache().findOrCreateDU(m, node.getContext(), options.getSSAOptions());
  }

  protected class SpecializedFactoryMethod extends SpecializedMethod {

    /**
     * List of synthetic invoke instructions we model for this specialized instance.
     */
    final private ArrayList calls = new ArrayList();

    /**
     * The method being modelled
     */
    private final IMethod method;

    /**
     * Context being modelled
     */
    private final Context context;

    /**
     * next free local value number;
     */
    private int nextLocal;

    /**
     * value number for integer constant 1
     */
    private int valueNumberForConstantOne = -1;

    private final SSAInstructionFactory insts = declaringClass.getClassLoader().getInstructionFactory();

    private void initValueNumberForConstantOne() {
      if (valueNumberForConstantOne == -1) {
        valueNumberForConstantOne = nextLocal++;
      }
    }

    protected SpecializedFactoryMethod(final SummarizedMethod m, Context context, final Set S) {
      super(m, m.getDeclaringClass(), m.isStatic(), true);

      this.context = context;
      if (DEBUG) {
        System.err.println(("Create SpecializedFactoryMethod " + m + S));
      }

      this.method = m;
      assert S != null;
      assert m.getDeclaringClass() != null : "null declaring class for " + m;

      // add original statements from the method summary
      nextLocal = addOriginalStatements(m);

      for (Iterator it = S.iterator(); it.hasNext();) {
        TypeReference type = (TypeReference) it.next();
        TypeAbstraction T = typeRef2TypeAbstraction(m.getClassHierarchy(), type);
        addStatementsForTypeAbstraction(T);
      }
    }

    protected void addStatementsForTypeAbstraction(TypeAbstraction T) {

      if (DEBUG) {
        System.err.println(("adding " + T + " to " + method));
      }
      T = interceptType(T);
      if (T == null) {
        return;
      }
      if ((T instanceof PointType) || (T instanceof ConeType)) {
        TypeReference ref = T.getType().getReference();
        NewSiteReference site = NewSiteReference.make(0, ref);

        if (DEBUG) {
          IClass klass = options.getClassTargetSelector().getAllocatedTarget(null, site);
          System.err.println(("Selected allocated target: " + klass + " for " + T));
        }
        if (T instanceof PointType) {
          if (!typesAllocated.contains(ref)) {
            addStatementsForConcreteType(ref);
          }
        } else if (T instanceof ConeType) {
          if (DEBUG) {
            System.err.println(("Cone clause for " + T));
          }
          if (((ConeType) T).isInterface()) {
            Set implementors = T.getType().getClassHierarchy().getImplementors(ref);
            if (DEBUG) {
              System.err.println(("Implementors for " + T + " " + implementors));
            }
            if (implementors.isEmpty()) {
              if (DEBUG) {
                System.err.println(("Found no implementors of type " + T));
              }
              Warnings.add(NoSubtypesWarning.create(T));
            }
            if (implementors.size() > CONE_BOUND) {
              Warnings.add(ManySubtypesWarning.create(T, implementors.size()));
            }

            addStatementsForSetOfTypes(implementors.iterator());
          } else {
            Collection subclasses = T.getType().getClassHierarchy().computeSubClasses(ref);
            if (DEBUG) {
              System.err.println(("Subclasses for " + T + " " + subclasses));
            }
            if (subclasses.isEmpty()) {
              if (DEBUG) {
                System.err.println(("Found no subclasses of type " + T));
              }
              Warnings.add(NoSubtypesWarning.create(T));
            }
            if (subclasses.size() > CONE_BOUND) {
              Warnings.add(ManySubtypesWarning.create(T, subclasses.size()));
            }
            addStatementsForSetOfTypes(subclasses.iterator());
          }
        } else {
          Assertions.UNREACHABLE("Unexpected type " + T.getClass());
        }
      } else if (T instanceof SetType) {
        // This code has clearly bitrotted, since iteratePoints() returns an Iterator
        // and we need an Iterator. Commenting out for now. --MS
        Assertions.UNREACHABLE();
        // addStatementsForSetOfTypes(((SetType) T).iteratePoints());
      } else {
        Assertions.UNREACHABLE("Unexpected type " + T.getClass());
      }
    }

    private TypeAbstraction interceptType(TypeAbstraction T) {
      TypeReference type = T.getType().getReference();
      if (type.equals(TypeReference.JavaIoSerializable)) {
        Warnings.add(IgnoreSerializableWarning.create());
        return null;
      } else {
        return T;
      }
    }

    /**
     * Set up a method summary which allocates and returns an instance of concrete type T.
     * 
     * @param T
     */
    private void addStatementsForConcreteType(final TypeReference T) {
      int alloc = addStatementsForConcreteSimpleType(T);
      if (alloc == -1) {
        return;
      }
      if (T.isArrayType()) {
        MethodReference init = MethodReference.findOrCreate(T, MethodReference.initAtom, MethodReference.defaultInitDesc);
        CallSiteReference site = CallSiteReference.make(getCallSiteForType(T), init, IInvokeInstruction.Dispatch.SPECIAL);
        int[] params = new int[1];
        params[0] = alloc;
        int exc = getExceptionsForType(T);
        SSAInvokeInstruction s = insts.InvokeInstruction(params, exc, site);
        calls.add(s);
        allInstructions.add(s);
      }
    }

    private int addOriginalStatements(SummarizedMethod m) {
      SSAInstruction[] original = m.getStatements(options.getSSAOptions());
      // local value number 1 is "this", so the next free value number is 2
      int nextLocal = 2;
      for (int i = 0; i < original.length; i++) {
        SSAInstruction s = original[i];
        allInstructions.add(s);
        if (s instanceof SSAInvokeInstruction) {
          calls.add(s);
        }
        if (s instanceof SSANewInstruction) {
          allocations.add(s);
        }
        for (int j = 0; j < s.getNumberOfDefs(); j++) {
          int def = s.getDef(j);
          if (def >= nextLocal) {
            nextLocal = def + 1;
          }
        }
        for (int j = 0; j < s.getNumberOfUses(); j++) {
          int use = s.getUse(j);
          if (use >= nextLocal) {
            nextLocal = use + 1;
          }
        }
      }
      return nextLocal;
    }

    private void addStatementsForSetOfTypes(Iterator it) {
      if (!it.hasNext()) { // Uh. No types. Hope the caller reported a warning.
        SSAReturnInstruction r = insts.ReturnInstruction(nextLocal, false);
        allInstructions.add(r);
      }

      for (; it.hasNext();) {
        IClass klass = it.next();
        TypeReference T = klass.getReference();
        if (klass.isAbstract() || klass.isInterface() || typesAllocated.contains(T)) {
          continue;
        }
        typesAllocated.add(T);
        int i = getLocalForType(T);
        NewSiteReference ref = NewSiteReference.make(getNewSiteForType(T), T);
        SSANewInstruction a = null;
        if (T.isArrayType()) {
          int[] sizes = new int[((ArrayClass)klass).getDimensionality()];
          initValueNumberForConstantOne();
          Arrays.fill(sizes, valueNumberForConstantOne);
          a = insts.NewInstruction(i, ref, sizes);

        } else {
          a = insts.NewInstruction(i, ref);
        }
        allocations.add(a);
        allInstructions.add(a);
        SSAReturnInstruction r = insts.ReturnInstruction(i, false);
        allInstructions.add(r);
        MethodReference init = MethodReference.findOrCreate(T, MethodReference.initAtom, MethodReference.defaultInitDesc);
        CallSiteReference site = CallSiteReference.make(getCallSiteForType(T), init, IInvokeInstruction.Dispatch.SPECIAL);
        int[] params = new int[1];
        params[0] = i;
        SSAInvokeInstruction s = insts.InvokeInstruction(params, getExceptionsForType(T), site);
        calls.add(s);
        allInstructions.add(s);
      }
    }

    public List getAllocationStatements() {
      return allocations;
    }

    public List getInvokeStatements() {
      return calls;
    }

    /**
     * Two specialized methods can be different, even if they represent the same source method. So, revert to object identity for
     * testing equality. TODO: this is non-optimal; could try to re-use specialized methods that have the same context.
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
      return this == obj;
    }

    @Override
    public int hashCode() { // TODO: change this to avoid non-determinism!
      return System.identityHashCode(this);
    }

    @Override
    public String toString() {
      return super.toString();
    }

    @Override
    public SSAInstruction[] getStatements() {
      SSAInstruction[] result = new SSAInstruction[allInstructions.size()];
      int i = 0;
      for (Iterator it = allInstructions.iterator(); it.hasNext();) {
        result[i++] = it.next();
      }
      return result;
    }

    @Override
    public IClass getDeclaringClass() {
      assert method.getDeclaringClass() != null : "null declaring class for original method " + method;
      return method.getDeclaringClass();
    }

    @Override
    public int getNumberOfParameters() {
      return method.getNumberOfParameters();
    }

    @Override
    public TypeReference getParameterType(int i) {
      return method.getParameterType(i);
    }

    /*
     * @see com.ibm.wala.classLoader.IMethod#getIR(com.ibm.wala.util.WarningSet)
     */
    @Override
    public IR makeIR(Context C, SSAOptions options) {
      SSAInstruction[] instrs = getStatements();
      Map constants = null;
      if (valueNumberForConstantOne > -1) {
        constants = HashMapFactory.make(1);
        constants.put(new Integer(valueNumberForConstantOne), new ConstantValue(new Integer(1)));
      }

      return new SyntheticIR(this, context, new InducedCFG(instrs, this, context), instrs, options, constants);
    }
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.analysis.typeInference.ConeType;
import com.ibm.wala.analysis.typeInference.PointType;
import com.ibm.wala.analysis.typeInference.SetType;
import com.ibm.wala.analysis.typeInference.TypeAbstraction;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.CodeScanner;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.SummarizedMethod;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.warnings.Warnings;

/**
 * Logic to interpret "factory" methods in context.
 */
class FactoryBypassInterpreter extends AbstractReflectionInterpreter {

  /**
   * A Map from CallerSiteContext -> Set represents the types a factory method might create in a particular context
   */
  private final Map> map = HashMapFactory.make();

  /**
   * A cache of synthetic method implementations, indexed by Context
   */
  private final Map syntheticMethodCache = HashMapFactory.make();

  /**
   * @param options governing analysis options
   */
  public FactoryBypassInterpreter(AnalysisOptions options, AnalysisCache cache) {
    this.options = options;
    this.cache = cache;
  }

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return cache.getSSACache().findOrCreateIR(m, node.getContext(), options.getSSAOptions());
  }

  private Set getTypesForContext(Context context) {
    // first try user spec
    // XMLReflectionReader spec = (XMLReflectionReader) userSpec;
    // if (spec != null && context instanceof CallerSiteContext) {
    // CallerSiteContext site = (CallerSiteContext) context;
    // MemberReference m = site.getCaller().getMethod().getReference();
    // ReflectionSummary summary = spec.getSummary(m);
    // if (summary != null) {
    // Set types = summary.getTypesForProgramLocation(site.getCallSite().getProgramCounter());
    // if (types != null) {
    // return types;
    // }
    // }
    // }

    Set types = map.get(context);
    return types;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getNumberOfStatements(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public int getNumberOfStatements(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return m.allInstructions.size();
  }
  /*
   * @see com.ibm.wala.ipa.callgraph.rta.RTAContextInterpreter#understands(com.ibm.wala.classLoader.IMethod,
   * com.ibm.wala.ipa.callgraph.Context)
   */
  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (node.getMethod().isSynthetic()) {
      SyntheticMethod s = (SyntheticMethod) node.getMethod();
      if (s.isFactoryMethod()) {
        return getTypesForContext(node.getContext()) != null;
      }
    }
    return false;

  }

  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    HashSet result = HashSetFactory.make(5);
    for (Iterator it = m.getAllocationStatements().iterator(); it.hasNext();) {
      SSANewInstruction s = (SSANewInstruction) it.next();
      result.add(s.getNewSite());
    }
    return result.iterator();
  }

  public Iterator getInvokeStatements(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return m.getInvokeStatements().iterator();
  }

  public Iterator iterateCallSites(CGNode node) {
    final Iterator I = getInvokeStatements(node);
    return new Iterator() {
      public boolean hasNext() {
        return I.hasNext();
      }

      public CallSiteReference next() {
        SSAInvokeInstruction s = (SSAInvokeInstruction) I.next();
        return s.getCallSite();
      }

      public void remove() {
        Assertions.UNREACHABLE();
      }
    };
  }

  public boolean recordType(IClassHierarchy cha, Context context, TypeReference type) {
    Set types = map.get(context);
    if (types == null) {
      types = HashSetFactory.make(2);
      map.put(context, types);
    }
    if (types.contains(type)) {
      return false;
    } else {
      types.add(type);
      // update any extant synthetic method
      SpecializedFactoryMethod m = syntheticMethodCache.get(context);
      if (m != null) {
        TypeAbstraction T = typeRef2TypeAbstraction(cha, type);
        m.addStatementsForTypeAbstraction(T);
        cache.getSSACache().invalidate(m, context);
      }
      return true;
    }
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#recordFactoryType(com.ibm.wala.ipa.callgraph.CGNode,
   * com.ibm.wala.classLoader.IClass)
   */
  public boolean recordFactoryType(CGNode node, IClass klass) {
    if (klass == null) {
      throw new IllegalArgumentException("klass is null");
    }
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    return recordType(node.getMethod().getClassHierarchy(), node.getContext(), klass.getReference());
  }

  public Iterator iterateFieldsRead(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.getFieldsRead(m).iterator();
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.getFieldsWritten(m).iterator();
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  private SpecializedFactoryMethod findOrCreateSpecializedFactoryMethod(CGNode node) {
    SpecializedFactoryMethod m = syntheticMethodCache.get(node.getContext());
    if (m == null) {
      Set types = getTypesForContext(node.getContext());
      m = new SpecializedFactoryMethod((SummarizedMethod) node.getMethod(), node.getContext(), types);
      syntheticMethodCache.put(node.getContext(), m);
    }
    return m;
  }

  public Set getCaughtExceptions(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.getCaughtExceptions(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  public boolean hasObjectArrayLoad(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.hasObjectArrayLoad(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return false;
    }
  }

  public boolean hasObjectArrayStore(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.hasObjectArrayStore(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return false;
    }
  }

  public Iterator iterateCastTypes(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    try {
      return CodeScanner.iterateCastTypes(m);
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return null;
    }
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getCFG(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    SpecializedFactoryMethod m = findOrCreateSpecializedFactoryMethod(node);
    return cache.getSSACache().findOrCreateDU(m, node.getContext(), options.getSSAOptions());
  }

  protected class SpecializedFactoryMethod extends SpecializedMethod {

    /**
            }
     * List of synthetic invoke instructions we model for this specialized instance.
     */
    final private ArrayList calls = new ArrayList();

    /**
     * The method being modelled
     */
    private final IMethod method;

    /**
     * Context being modelled
     */
    private final Context context;

    /**
     * next free local value number;
     */
    private int nextLocal;

    /**
     * value number for integer constant 1
     */
    private int valueNumberForConstantOne = -1;

    private final SSAInstructionFactory insts = declaringClass.getClassLoader().getInstructionFactory();

    private void initValueNumberForConstantOne() {
      if (valueNumberForConstantOne == -1) {
        valueNumberForConstantOne = nextLocal++;
      }
    }

    protected SpecializedFactoryMethod(final SummarizedMethod m, Context context, final Set S) {
      super(m, m.getDeclaringClass(), m.isStatic(), true);

      this.context = context;
      if (DEBUG) {
        System.err.println(("Create SpecializedFactoryMethod " + m + S));
      }

      this.method = m;
      assert S != null;
      assert m.getDeclaringClass() != null : "null declaring class for " + m;

      // add original statements from the method summary
      nextLocal = addOriginalStatements(m);

      for (Iterator it = S.iterator(); it.hasNext();) {
        TypeReference type = (TypeReference) it.next();
        TypeAbstraction T = typeRef2TypeAbstraction(m.getClassHierarchy(), type);
        addStatementsForTypeAbstraction(T);
      }
    }

    protected void addStatementsForTypeAbstraction(TypeAbstraction T) {

      if (DEBUG) {
        System.err.println(("adding " + T + " to " + method));
      }
      T = interceptType(T);
      if (T == null) {
        return;
      }
      if ((T instanceof PointType) || (T instanceof ConeType)) {
        TypeReference ref = T.getType().getReference();
        NewSiteReference site = NewSiteReference.make(0, ref);

        if (DEBUG) {
          IClass klass = options.getClassTargetSelector().getAllocatedTarget(null, site);
          System.err.println(("Selected allocated target: " + klass + " for " + T));
        }
        if (T instanceof PointType) {
          if (!typesAllocated.contains(ref)) {
            addStatementsForConcreteType(ref);
          }
        } else if (T instanceof ConeType) {
          if (DEBUG) {
            System.err.println(("Cone clause for " + T));
          }
          if (((ConeType) T).isInterface()) {
            Set implementors = T.getType().getClassHierarchy().getImplementors(ref);
            if (DEBUG) {
              System.err.println(("Implementors for " + T + " " + implementors));
            }
            if (implementors.isEmpty()) {
              if (DEBUG) {
                System.err.println(("Found no implementors of type " + T));
              }
              Warnings.add(NoSubtypesWarning.create(T));
            }
            if (implementors.size() > CONE_BOUND) {
              Warnings.add(ManySubtypesWarning.create(T, implementors.size()));
            }

            addStatementsForSetOfTypes(implementors.iterator());
          } else {
            Collection subclasses = T.getType().getClassHierarchy().computeSubClasses(ref);
            if (DEBUG) {
              System.err.println(("Subclasses for " + T + " " + subclasses));
            if (subclasses.isEmpty()) {
              if (DEBUG) {
                System.err.println(("Found no subclasses of type " + T));
              }
              Warnings.add(NoSubtypesWarning.create(T));
            }
            if (subclasses.size() > CONE_BOUND) {
              Warnings.add(ManySubtypesWarning.create(T, subclasses.size()));
            }
            addStatementsForSetOfTypes(subclasses.iterator());
          }
        } else {
          Assertions.UNREACHABLE("Unexpected type " + T.getClass());
        }
      } else if (T instanceof SetType) {
        // This code has clearly bitrotted, since iteratePoints() returns an Iterator
        // and we need an Iterator. Commenting out for now. --MS
        Assertions.UNREACHABLE();
        // addStatementsForSetOfTypes(((SetType) T).iteratePoints());
      } else {
        Assertions.UNREACHABLE("Unexpected type " + T.getClass());
      }
    }

    private TypeAbstraction interceptType(TypeAbstraction T) {
      TypeReference type = T.getType().getReference();
      if (type.equals(TypeReference.JavaIoSerializable)) {
        Warnings.add(IgnoreSerializableWarning.create());
        return null;
      } else {
        return T;
      }
    }

    /**
     * Set up a method summary which allocates and returns an instance of concrete type T.
     * 
     * @param T
     */
    private void addStatementsForConcreteType(final TypeReference T) {
      int alloc = addStatementsForConcreteSimpleType(T);
      if (alloc == -1) {
        return;
      }
      if (T.isArrayType()) {
        MethodReference init = MethodReference.findOrCreate(T, MethodReference.initAtom, MethodReference.defaultInitDesc);
        CallSiteReference site = CallSiteReference.make(getCallSiteForType(T), init, IInvokeInstruction.Dispatch.SPECIAL);
        int[] params = new int[1];
        params[0] = alloc;
        int exc = getExceptionsForType(T);
        SSAInvokeInstruction s = insts.InvokeInstruction(allInstructions.size(), params, exc, site);
        calls.add(s);
        allInstructions.add(s);
      }
    }

    private int addOriginalStatements(SummarizedMethod m) {
      SSAInstruction[] original = m.getStatements(options.getSSAOptions());
      // local value number 1 is "this", so the next free value number is 2
      int nextLocal = 2;
      for (int i = 0; i < original.length; i++) {
        SSAInstruction s = original[i];
        allInstructions.add(s);
        if (s instanceof SSAInvokeInstruction) {
          calls.add(s);
        }
        if (s instanceof SSANewInstruction) {
          allocations.add(s);
        }
        for (int j = 0; j < s.getNumberOfDefs(); j++) {
          int def = s.getDef(j);
          if (def >= nextLocal) {
            nextLocal = def + 1;
          }
        }
        for (int j = 0; j < s.getNumberOfUses(); j++) {
          int use = s.getUse(j);
          if (use >= nextLocal) {
            nextLocal = use + 1;
          }
        }
      }
      return nextLocal;
    }

    private void addStatementsForSetOfTypes(Iterator it) {
      if (!it.hasNext()) { // Uh. No types. Hope the caller reported a warning.
        SSAReturnInstruction r = insts.ReturnInstruction(allInstructions.size(), nextLocal, false);
        allInstructions.add(r);
      }

      for (; it.hasNext();) {
        IClass klass = it.next();
        TypeReference T = klass.getReference();
        if (klass.isAbstract() || klass.isInterface() || typesAllocated.contains(T)) {
          continue;
        }
        typesAllocated.add(T);
        int i = getLocalForType(T);
        NewSiteReference ref = NewSiteReference.make(getNewSiteForType(T), T);
        SSANewInstruction a = null;
        if (T.isArrayType()) {
          int[] sizes = new int[((ArrayClass)klass).getDimensionality()];
          initValueNumberForConstantOne();
          Arrays.fill(sizes, valueNumberForConstantOne);
          a = insts.NewInstruction(allInstructions.size(), i, ref, sizes);

        } else {
          a = insts.NewInstruction(allInstructions.size(), i, ref);
        }
        allocations.add(a);
        allInstructions.add(a);
        SSAReturnInstruction r = insts.ReturnInstruction(allInstructions.size(), i, false);
        allInstructions.add(r);
        MethodReference init = MethodReference.findOrCreate(T, MethodReference.initAtom, MethodReference.defaultInitDesc);
        CallSiteReference site = CallSiteReference.make(getCallSiteForType(T), init, IInvokeInstruction.Dispatch.SPECIAL);
        int[] params = new int[1];
        params[0] = i;
        SSAInvokeInstruction s = insts.InvokeInstruction(allInstructions.size(), params, getExceptionsForType(T), site);
        calls.add(s);
        allInstructions.add(s);
      }
    }

    public List getAllocationStatements() {
      return allocations;
    }

    public List getInvokeStatements() {
      return calls;
    }

    /**
     * Two specialized methods can be different, even if they represent the same source method. So, revert to object identity for
     * testing equality. TODO: this is non-optimal; could try to re-use specialized methods that have the same context.
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
      return this == obj;
    }

    @Override
    public int hashCode() { // TODO: change this to avoid non-determinism!
      return System.identityHashCode(this);
    }

    @Override
    public String toString() {
      return super.toString();
    }

    @Override
    public SSAInstruction[] getStatements() {
      SSAInstruction[] result = new SSAInstruction[allInstructions.size()];
      int i = 0;
      for (Iterator it = allInstructions.iterator(); it.hasNext();) {
        result[i++] = it.next();
      }
      return result;
    }

    @Override
    public IClass getDeclaringClass() {
      assert method.getDeclaringClass() != null : "null declaring class for original method " + method;
      return method.getDeclaringClass();
    }

    @Override
    public int getNumberOfParameters() {
      return method.getNumberOfParameters();
    }

    @Override
    public TypeReference getParameterType(int i) {
      return method.getParameterType(i);
    }

    /*
     * @see com.ibm.wala.classLoader.IMethod#getIR(com.ibm.wala.util.WarningSet)
     */
    @Override
    public IR makeIR(Context C, SSAOptions options) {
      SSAInstruction[] instrs = getStatements();
      Map constants = null;
      if (valueNumberForConstantOne > -1) {
        constants = HashMapFactory.make(1);
        constants.put(new Integer(valueNumberForConstantOne), new ConstantValue(new Integer(1)));
      }

      return new SyntheticIR(this, context, new InducedCFG(instrs, this, context), instrs, options, constants);
    }
  }
}
File
FactoryBypassInterpreter.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
    if (tr != null) {
  }

<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;

/**
 * {@link SSAContextInterpreter} specialized to interpret Object.getClass() in a {@link JavaTypeContext}
 */
public class GetClassContextInterpeter implements SSAContextInterpreter {

/** BEGIN Custom change: caching */
  private final Map cache = HashMapFactory.make();
  
/** END Custom change: caching */
  private static final boolean DEBUG = false;

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
/** BEGIN Custom change: caching */
    
    final JavaTypeContext context = (JavaTypeContext) node.getContext();
    final IMethod method = node.getMethod();
    final String hashKey = method.toString() + "@" + context.toString();
    
    IR result = cache.get(hashKey);
    
    if (result == null) {
      result = makeIR(method, context);
      cache.put(hashKey, result);
    }
    
/** END Custom change: caching */
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (!(node.getContext() instanceof JavaTypeContext)) {
      return false;
    }
    return node.getMethod().getReference().equals(GetClassContextSelector.GET_CLASS);
  }

  public Iterator iterateNewSites(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateCallSites(CGNode node) {
    return EmptyIterator.instance();
  }

  private SSAInstruction[] makeStatements(JavaTypeContext context) {
    ArrayList statements = new ArrayList();
    int nextLocal = 2;
    int retValue = nextLocal++;
    TypeReference tr = context.getType().getTypeReference();
    SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory();
      SSALoadMetadataInstruction l = insts.LoadMetadataInstruction(statements.size(), retValue, TypeReference.JavaLangClass, tr);
      statements.add(l);
      SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), retValue, false);
      statements.add(R);
    }
    SSAInstruction[] result = new SSAInstruction[statements.size()];
    Iterator it = statements.iterator();
    for (int i = 0; i < result.length; i++) {
      result[i] = it.next();
    }
    return result;
  }

  private IR makeIR(IMethod method, JavaTypeContext context) {
    SSAInstruction instrs[] = makeStatements(context);
    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Iterator;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;

/**
 * {@link SSAContextInterpreter} specialized to interpret Object.getClass() in a {@link JavaTypeContext}
 */
public class GetClassContextInterpeter implements SSAContextInterpreter {

  private static final boolean DEBUG = false;

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
    IR result = makeIR(node.getMethod(), (JavaTypeContext) node.getContext());
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (!(node.getContext() instanceof JavaTypeContext)) {
      return false;
    }
    return node.getMethod().getReference().equals(GetClassContextSelector.GET_CLASS);
  }

  public Iterator iterateNewSites(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateCallSites(CGNode node) {
    return EmptyIterator.instance();
  }

  private SSAInstruction[] makeStatements(JavaTypeContext context) {
    ArrayList statements = new ArrayList();
    int nextLocal = 2;
    int retValue = nextLocal++;
    TypeReference tr = context.getType().getTypeReference();
    SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory();
    if (tr != null) {
      SSALoadMetadataInstruction l = insts.LoadMetadataInstruction(retValue, TypeReference.JavaLangClass, tr);
      statements.add(l);
      SSAReturnInstruction R = insts.ReturnInstruction(retValue, false);
      statements.add(R);
    }
    SSAInstruction[] result = new SSAInstruction[statements.size()];
    Iterator it = statements.iterator();
    for (int i = 0; i < result.length; i++) {
      result[i] = it.next();
    }
    return result;
  }

  private IR makeIR(IMethod method, JavaTypeContext context) {
    SSAInstruction instrs[] = makeStatements(context);
    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;

/**
 * {@link SSAContextInterpreter} specialized to interpret Object.getClass() in a {@link JavaTypeContext}
 */
public class GetClassContextInterpeter implements SSAContextInterpreter {

/** BEGIN Custom change: caching */
  private final Map cache = HashMapFactory.make();
  
/** END Custom change: caching */
  private static final boolean DEBUG = false;

  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
/** BEGIN Custom change: caching */
    
    final JavaTypeContext context = (JavaTypeContext) node.getContext();
    final IMethod method = node.getMethod();
    final String hashKey = method.toString() + "@" + context.toString();
    
    IR result = cache.get(hashKey);
    
    if (result == null) {
      result = makeIR(method, context);
      cache.put(hashKey, result);
    }
    
/** END Custom change: caching */
    return result;
  }

  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (!(node.getContext() instanceof JavaTypeContext)) {
      return false;
    }
    return node.getMethod().getReference().equals(GetClassContextSelector.GET_CLASS);
  }

  public Iterator iterateNewSites(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateCallSites(CGNode node) {
    return EmptyIterator.instance();
  }

  private SSAInstruction[] makeStatements(JavaTypeContext context) {
    ArrayList statements = new ArrayList();
    int nextLocal = 2;
    int retValue = nextLocal++;
    TypeReference tr = context.getType().getTypeReference();
    SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory();
    if (tr != null) {
      SSALoadMetadataInstruction l = insts.LoadMetadataInstruction(statements.size(), retValue, TypeReference.JavaLangClass, tr);
      statements.add(l);
      SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), retValue, false);
      statements.add(R);
    }
    SSAInstruction[] result = new SSAInstruction[statements.size()];
    Iterator it = statements.iterator();
    for (int i = 0; i < result.length; i++) {
      result[i] = it.next();
    }
    return result;
  }

  private IR makeIR(IMethod method, JavaTypeContext context) {
    SSAInstruction instrs[] = makeStatements(context);
    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), null);
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
File
GetClassContextInterpeter.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
        result.add(m);
          constants);
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.debug.Assertions;

/**
 * An {@link SSAContextInterpreter} specialized to interpret methods on java.lang.Class in a {@link JavaTypeContext} which
 * represents the point-type of the class object created by the call.
 * 
 * Currently supported methods:
 * 
    *
  • getConstructor *
  • getConstructors *
  • getMethod *
  • getMethods *
  • getDeclaredConstructor *
  • getDeclaredConstructors *
  • getDeclaredMethod *
  • getDeclaredMethods *
*/ public class JavaLangClassContextInterpreter implements SSAContextInterpreter { public final static MethodReference GET_CONSTRUCTOR = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_CONSTRUCTORS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getConstructors", "()[Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_METHOD = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); public final static MethodReference GET_METHODS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getMethods", "()[Ljava/lang/reflect/Method;"); public final static MethodReference GET_DECLARED_CONSTRUCTOR = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_DECLARED_CONSTRUCTORS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_DECLARED_METHOD = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); public final static MethodReference GET_DECLARED_METHODS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;"); private static final boolean DEBUG = false; /** BEGIN Custom change: caching */ private final Map cache = HashMapFactory.make(); /** END Custom change: caching */ /* * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getIR(com.ibm.wala.ipa.callgraph.CGNode) */ public IR getIR(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } assert understands(node); if (DEBUG) { System.err.println("generating IR for " + node); } /** BEGIN Custom change: caching */ final JavaTypeContext context = (JavaTypeContext) node.getContext(); final IMethod method = node.getMethod(); final String hashKey = method.toString() + "@" + context.toString(); IR result = cache.get(hashKey); if (result == null) { result = makeIR(method, context); if (result == null) { Assertions.UNREACHABLE("Unexpected method " + node); } cache.put(hashKey, result); } return result; } private IR makeIR(IMethod method, JavaTypeContext context) { Map constants = HashMapFactory.make(); if (method.getReference().equals(GET_CONSTRUCTOR)) { SSAInstruction instrs[] = makeGetCtorStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_CONSTRUCTORS)) { SSAInstruction instrs[] = makeGetCtorsStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_METHOD)) { SSAInstruction instrs[] = makeGetMethodStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_METHODS)) { SSAInstruction instrs[] = makeGetMethodsStatments(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_CONSTRUCTOR)) { SSAInstruction instrs[] = makeGetDeclCtorStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_CONSTRUCTORS)) { SSAInstruction instrs[] = makeGetDeclCtorsStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_METHOD)) { SSAInstruction instrs[] = makeGetDeclaredMethodStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_METHODS)) { SSAInstruction instrs[] = makeGetDeclaredMethodsStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } Assertions.UNREACHABLE("Unexpected method " + method); return null; } */ /** END Custom change: caching */ /* * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getNumberOfStatements(com.ibm.wala.ipa.callgraph.CGNode) */ public int getNumberOfStatements(CGNode node) { assert understands(node); return getIR(node).getInstructions().length; } /* * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode) */ public boolean understands(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } if (!(node.getContext() instanceof JavaTypeContext)) { return false; } MethodReference mRef = node.getMethod().getReference(); return mRef.equals(GET_CONSTRUCTOR) || mRef.equals(GET_CONSTRUCTORS) || mRef.equals(GET_METHOD) || mRef.equals(GET_METHODS) || mRef.equals(GET_DECLARED_CONSTRUCTOR) || mRef.equals(GET_DECLARED_CONSTRUCTORS) || mRef.equals(GET_DECLARED_METHOD) || mRef.equals(GET_DECLARED_METHODS); } public Iterator iterateNewSites(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } assert understands(node); JavaTypeContext context = (JavaTypeContext) node.getContext(); TypeReference tr = context.getType().getTypeReference(); if (tr != null) { return new NonNullSingletonIterator(NewSiteReference.make(0, tr)); } return EmptyIterator.instance(); } public Iterator iterateCallSites(CGNode node) { assert understands(node); return EmptyIterator.instance(); } /** * Get all non-constructor, non-class-initializer methods declared by a class */ private Collection getDeclaredNormalMethods(IClass cls) { Collection result = HashSetFactory.make(); for (IMethod m : cls.getDeclaredMethods()) { if (!m.isInit() && !m.isClinit()) { result.add(m); } } return result; } /** * Get all non-constructor, non-class-initializer methods declared by a class and all its superclasses */ private Collection getAllNormalPublicMethods(IClass cls) { Collection result = HashSetFactory.make(); Collection allMethods = null; allMethods = cls.getAllMethods(); for (IMethod m : allMethods) { if (!m.isInit() && !m.isClinit() && m.isPublic()) { result.add(m); } } return result; } /** * Get all the constructors of a class */ private Collection getConstructors(IClass cls) { Collection result = HashSetFactory.make(); for (IMethod m : cls.getDeclaredMethods()) { if (m.isInit()) { result.add(m); } } return result; } /** * Get all the public constructors of a class */ private Collection getPublicConstructors(IClass cls) { Collection result = HashSetFactory.make(); for (IMethod m : cls.getDeclaredMethods()) { if (m.isInit() && m.isPublic()) { result.add(m); } } return result; } /** * create statements for methods like getConstructors() and getMethods(), which return an array of methods. * * @param returnValues the possible return values for this method. private SSAInstruction[] getMethodArrayStatements(MethodReference ref, Collection returnValues, JavaTypeContext context, Map constants) { ArrayList statements = new ArrayList(); int nextLocal = ref.getNumberOfParameters() + 2; int retValue = nextLocal++; IClass cls = context.getType().getType(); SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory(); if (cls != null) { TypeReference arrType = ref.getReturnType(); NewSiteReference site = new NewSiteReference(retValue, arrType); int sizeVn = nextLocal++; constants.put(sizeVn, new ConstantValue(returnValues.size())); SSANewInstruction allocArr = insts.NewInstruction(statements.size(), retValue, site, new int[] { sizeVn }); statements.add(allocArr); int i = 0; for (IMethod m : returnValues) { int c = nextLocal++; constants.put(c, new ConstantValue(m)); int index = i++; int indexVn = nextLocal++; constants.put(indexVn, new ConstantValue(index)); SSAArrayStoreInstruction store = insts .ArrayStoreInstruction(statements.size(), retValue, indexVn, c, TypeReference.JavaLangReflectConstructor); statements.add(store); } SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), retValue, false); statements.add(R); } else { // SJF: This is incorrect. TODO: fix and enable. // SSAThrowInstruction t = insts.ThrowInstruction(retValue); // statements.add(t); } SSAInstruction[] result = new SSAInstruction[statements.size()]; Iterator it = statements.iterator(); for (int i = 0; i < result.length; i++) { result[i] = it.next(); } return result; } /** * create statements for methods like getConstructor() and getDeclaredMethod(), which return a single method. This creates a * return statement for each possible return value, each of which is a {@link ConstantValue} for an {@link IMethod}. * * @param returnValues the possible return values for this method. */ private SSAInstruction[] getParticularMethodStatements(MethodReference ref, Collection returnValues, JavaTypeContext context, Map constants) { ArrayList statements = new ArrayList(); int nextLocal = ref.getNumberOfParameters() + 2; IClass cls = context.getType().getType(); SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory(); if (cls != null) { for (IMethod m : returnValues) { int c = nextLocal++; constants.put(c, new ConstantValue(m)); SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), c, false); statements.add(R); } } else { // SJF: This is incorrect. TODO: fix and enable. // SSAThrowInstruction t = insts.ThrowInstruction(retValue); // statements.add(t); } SSAInstruction[] result = new SSAInstruction[statements.size()]; Iterator it = statements.iterator(); for (int i = 0; i < result.length; i++) { result[i] = it.next(); } return result; } /** * create statements for getConstructor() */ private SSAInstruction[] makeGetCtorStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_CONSTRUCTOR, null, context, constants); } else { return getParticularMethodStatements(GET_CONSTRUCTOR, getPublicConstructors(cls), context, constants); } } // TODO private SSAInstruction[] makeGetCtorsStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, null, context, constants); } else { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, getPublicConstructors(cls), context, constants); } } private SSAInstruction[] makeGetMethodStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_METHOD, null, context, constants); } else { return getParticularMethodStatements(GET_METHOD, getAllNormalPublicMethods(cls), context, constants); } } private SSAInstruction[] makeGetMethodsStatments(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_METHODS, null, context, constants); } else { return getMethodArrayStatements(GET_METHODS, getAllNormalPublicMethods(cls), context, constants); } } /** * create statements for getConstructor() */ private SSAInstruction[] makeGetDeclCtorStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_DECLARED_CONSTRUCTOR, null, context, constants); } else { return getParticularMethodStatements(GET_DECLARED_CONSTRUCTOR, getConstructors(cls), context, constants); } } private SSAInstruction[] makeGetDeclCtorsStatements(JavaTypeContext context, Map constants) { } IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, null, context, constants); } else { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, getConstructors(cls), context, constants); } } /** * create statements for getDeclaredMethod() */ private SSAInstruction[] makeGetDeclaredMethodStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_DECLARED_METHOD, null, context, constants); } else { return getParticularMethodStatements(GET_DECLARED_METHOD, getDeclaredNormalMethods(cls), context, constants); } } /** * create statements for getDeclaredMethod() */ private SSAInstruction[] makeGetDeclaredMethodsStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_DECLARED_METHODS, null, context, constants); } else { return getMethodArrayStatements(GET_DECLARED_METHODS, getDeclaredNormalMethods(cls), context, constants); } } public boolean recordFactoryType(CGNode node, IClass klass) { return false; } public Iterator iterateFieldsRead(CGNode node) { return EmptyIterator.instance(); } public Iterator iterateFieldsWritten(CGNode node) { return EmptyIterator.instance(); } public ControlFlowGraph getCFG(CGNode N) { return getIR(N).getControlFlowGraph(); } public DefUse getDU(CGNode node) { return new DefUse(getIR(node)); } } ======= /******************************************************************************* * Copyright (c) 2008 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.analysis.reflection; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; import com.ibm.wala.cfg.ControlFlowGraph; import com.ibm.wala.cfg.InducedCFG; import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.classLoader.NewSiteReference; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter; import com.ibm.wala.ipa.summaries.SyntheticIR; import com.ibm.wala.ssa.ConstantValue; import com.ibm.wala.ssa.DefUse; import com.ibm.wala.ssa.IR; import com.ibm.wala.ssa.ISSABasicBlock; import com.ibm.wala.ssa.SSAArrayStoreInstruction; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSAInstructionFactory; import com.ibm.wala.ssa.SSANewInstruction; import com.ibm.wala.ssa.SSAOptions; import com.ibm.wala.ssa.SSAReturnInstruction; import com.ibm.wala.types.FieldReference; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.collections.EmptyIterator; import com.ibm.wala.util.collections.HashMapFactory; import com.ibm.wala.util.collections.HashSetFactory; if (m.isInit()) { import com.ibm.wala.util.collections.NonNullSingletonIterator; import com.ibm.wala.util.debug.Assertions; /** * An {@link SSAContextInterpreter} specialized to interpret methods on java.lang.Class in a {@link JavaTypeContext} which * represents the point-type of the class object created by the call. * * Currently supported methods: *
    *
  • getConstructor *
  • getConstructors *
  • getMethod *
  • getMethods *
  • getDeclaredConstructor *
  • getDeclaredConstructors *
  • getDeclaredMethod *
  • getDeclaredMethods *
*/ public class JavaLangClassContextInterpreter implements SSAContextInterpreter { public final static MethodReference GET_CONSTRUCTOR = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_CONSTRUCTORS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getConstructors", "()[Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_METHOD = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); public final static MethodReference GET_METHODS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getMethods", "()[Ljava/lang/reflect/Method;"); public final static MethodReference GET_DECLARED_CONSTRUCTOR = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_DECLARED_CONSTRUCTORS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_DECLARED_METHOD = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); public final static MethodReference GET_DECLARED_METHODS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;"); private static final boolean DEBUG = false; /* * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getIR(com.ibm.wala.ipa.callgraph.CGNode) */ public IR getIR(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } assert understands(node); if (DEBUG) { System.err.println("generating IR for " + node); } IMethod method = node.getMethod(); JavaTypeContext context = (JavaTypeContext) node.getContext(); Map constants = HashMapFactory.make(); if (method.getReference().equals(GET_CONSTRUCTOR)) { SSAInstruction instrs[] = makeGetCtorStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_CONSTRUCTORS)) { SSAInstruction instrs[] = makeGetCtorsStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_METHOD)) { SSAInstruction instrs[] = makeGetMethodStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_METHODS)) { SSAInstruction instrs[] = makeGetMethodsStatments(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_CONSTRUCTOR)) { SSAInstruction instrs[] = makeGetDeclCtorStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), if (method.getReference().equals(GET_DECLARED_CONSTRUCTORS)) { SSAInstruction instrs[] = makeGetDeclCtorsStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_METHOD)) { SSAInstruction instrs[] = makeGetDeclaredMethodStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_METHODS)) { SSAInstruction instrs[] = makeGetDeclaredMethodsStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } Assertions.UNREACHABLE("Unexpected method " + node); return null; } /* * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getNumberOfStatements(com.ibm.wala.ipa.callgraph.CGNode) */ public int getNumberOfStatements(CGNode node) { assert understands(node); return getIR(node).getInstructions().length; } /* * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode) */ public boolean understands(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } if (!(node.getContext() instanceof JavaTypeContext)) { return false; } MethodReference mRef = node.getMethod().getReference(); return mRef.equals(GET_CONSTRUCTOR) || mRef.equals(GET_CONSTRUCTORS) || mRef.equals(GET_METHOD) || mRef.equals(GET_METHODS) || mRef.equals(GET_DECLARED_CONSTRUCTOR) || mRef.equals(GET_DECLARED_CONSTRUCTORS) || mRef.equals(GET_DECLARED_METHOD) || mRef.equals(GET_DECLARED_METHODS); } public Iterator iterateNewSites(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } assert understands(node); JavaTypeContext context = (JavaTypeContext) node.getContext(); TypeReference tr = context.getType().getTypeReference(); if (tr != null) { return new NonNullSingletonIterator(NewSiteReference.make(0, tr)); } return EmptyIterator.instance(); } public Iterator iterateCallSites(CGNode node) { assert understands(node); return EmptyIterator.instance(); } /** * Get all non-constructor, non-class-initializer methods declared by a class */ private Collection getDeclaredNormalMethods(IClass cls) { Collection result = HashSetFactory.make(); for (IMethod m : cls.getDeclaredMethods()) { if (!m.isInit() && !m.isClinit()) { result.add(m); } } return result; } /** * Get all non-constructor, non-class-initializer methods declared by a class and all its superclasses */ private Collection getAllNormalPublicMethods(IClass cls) { Collection result = HashSetFactory.make(); Collection allMethods = null; allMethods = cls.getAllMethods(); for (IMethod m : allMethods) { if (!m.isInit() && !m.isClinit() && m.isPublic()) { result.add(m); } } return result; } /** * Get all the constructors of a class */ private Collection getConstructors(IClass cls) { Collection result = HashSetFactory.make(); for (IMethod m : cls.getDeclaredMethods()) { } } return result; } /** * Get all the public constructors of a class */ private Collection getPublicConstructors(IClass cls) { Collection result = HashSetFactory.make(); for (IMethod m : cls.getDeclaredMethods()) { if (m.isInit() && m.isPublic()) { result.add(m); } } return result; } /** * create statements for methods like getConstructors() and getMethods(), which return an array of methods. * * @param returnValues the possible return values for this method. */ private SSAInstruction[] getMethodArrayStatements(MethodReference ref, Collection returnValues, JavaTypeContext context, Map constants) { ArrayList statements = new ArrayList(); int nextLocal = ref.getNumberOfParameters() + 2; int retValue = nextLocal++; IClass cls = context.getType().getType(); SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory(); if (cls != null) { TypeReference arrType = ref.getReturnType(); NewSiteReference site = new NewSiteReference(retValue, arrType); int sizeVn = nextLocal++; constants.put(sizeVn, new ConstantValue(returnValues.size())); SSANewInstruction allocArr = insts.NewInstruction(retValue, site, new int[] { sizeVn }); statements.add(allocArr); int i = 0; for (IMethod m : returnValues) { int c = nextLocal++; constants.put(c, new ConstantValue(m)); int index = i++; int indexVn = nextLocal++; constants.put(indexVn, new ConstantValue(index)); SSAArrayStoreInstruction store = insts .ArrayStoreInstruction(retValue, indexVn, c, TypeReference.JavaLangReflectConstructor); statements.add(store); } SSAReturnInstruction R = insts.ReturnInstruction(retValue, false); statements.add(R); } else { // SJF: This is incorrect. TODO: fix and enable. // SSAThrowInstruction t = insts.ThrowInstruction(retValue); // statements.add(t); } SSAInstruction[] result = new SSAInstruction[statements.size()]; Iterator it = statements.iterator(); for (int i = 0; i < result.length; i++) { result[i] = it.next(); } return result; } /** * create statements for methods like getConstructor() and getDeclaredMethod(), which return a single method. This creates a * return statement for each possible return value, each of which is a {@link ConstantValue} for an {@link IMethod}. * * @param returnValues the possible return values for this method. */ private SSAInstruction[] getParticularMethodStatements(MethodReference ref, Collection returnValues, JavaTypeContext context, Map constants) { ArrayList statements = new ArrayList(); int nextLocal = ref.getNumberOfParameters() + 2; IClass cls = context.getType().getType(); SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory(); if (cls != null) { for (IMethod m : returnValues) { int c = nextLocal++; constants.put(c, new ConstantValue(m)); SSAReturnInstruction R = insts.ReturnInstruction(c, false); statements.add(R); } } else { // SJF: This is incorrect. TODO: fix and enable. // SSAThrowInstruction t = insts.ThrowInstruction(retValue); // statements.add(t); } SSAInstruction[] result = new SSAInstruction[statements.size()]; Iterator it = statements.iterator(); for (int i = 0; i < result.length; i++) { result[i] = it.next(); } return result; } /** * create statements for getConstructor() */ private SSAInstruction[] makeGetCtorStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_CONSTRUCTOR, null, context, constants); } else { return getParticularMethodStatements(GET_CONSTRUCTOR, getPublicConstructors(cls), context, constants); } } // TODO private SSAInstruction[] makeGetCtorsStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, null, context, constants); } else { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, getPublicConstructors(cls), context, constants); } } private SSAInstruction[] makeGetMethodStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_METHOD, null, context, constants); } else { return getParticularMethodStatements(GET_METHOD, getAllNormalPublicMethods(cls), context, constants); } } private SSAInstruction[] makeGetMethodsStatments(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_METHODS, null, context, constants); } else { return getMethodArrayStatements(GET_METHODS, getAllNormalPublicMethods(cls), context, constants); } } /** * create statements for getConstructor() */ private SSAInstruction[] makeGetDeclCtorStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_DECLARED_CONSTRUCTOR, null, context, constants); } else { return getParticularMethodStatements(GET_DECLARED_CONSTRUCTOR, getConstructors(cls), context, constants); } } private SSAInstruction[] makeGetDeclCtorsStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, null, context, constants); } else { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, getConstructors(cls), context, constants); } } /** * create statements for getDeclaredMethod() */ private SSAInstruction[] makeGetDeclaredMethodStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_DECLARED_METHOD, null, context, constants); } else { return getParticularMethodStatements(GET_DECLARED_METHOD, getDeclaredNormalMethods(cls), context, constants); } } /** * create statements for getDeclaredMethod() */ private SSAInstruction[] makeGetDeclaredMethodsStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_DECLARED_METHODS, null, context, constants); } else { return getMethodArrayStatements(GET_DECLARED_METHODS, getDeclaredNormalMethods(cls), context, constants); } } public boolean recordFactoryType(CGNode node, IClass klass) { return false; } public Iterator iterateFieldsRead(CGNode node) { return EmptyIterator.instance(); } public Iterator iterateFieldsWritten(CGNode node) { return EmptyIterator.instance(); } public ControlFlowGraph getCFG(CGNode N) { return getIR(N).getControlFlowGraph(); } public DefUse getDU(CGNode node) { return new DefUse(getIR(node)); } } >>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
    return false;
  }

/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.debug.Assertions;

/**
 * An {@link SSAContextInterpreter} specialized to interpret methods on java.lang.Class in a {@link JavaTypeContext} which
 * represents the point-type of the class object created by the call.
 * 
 * Currently supported methods:
 * 
    *
  • getConstructor *
  • getConstructors *
  • getMethod *
  • getMethods *
  • getDeclaredConstructor *
  • getDeclaredConstructors *
  • getDeclaredMethod *
  • getDeclaredMethods *
*/ public class JavaLangClassContextInterpreter implements SSAContextInterpreter { public final static MethodReference GET_CONSTRUCTOR = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_CONSTRUCTORS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getConstructors", "()[Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_METHOD = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); public final static MethodReference GET_METHODS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getMethods", "()[Ljava/lang/reflect/Method;"); public final static MethodReference GET_DECLARED_CONSTRUCTOR = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_DECLARED_CONSTRUCTORS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;"); public final static MethodReference GET_DECLARED_METHOD = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); public final static MethodReference GET_DECLARED_METHODS = MethodReference.findOrCreate(TypeReference.JavaLangClass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;"); private static final boolean DEBUG = false; /** BEGIN Custom change: caching */ private final Map cache = HashMapFactory.make(); /** END Custom change: caching */ /* * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getIR(com.ibm.wala.ipa.callgraph.CGNode) */ public IR getIR(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } assert understands(node); if (DEBUG) { System.err.println("generating IR for " + node); } /** BEGIN Custom change: caching */ final JavaTypeContext context = (JavaTypeContext) node.getContext(); final IMethod method = node.getMethod(); final String hashKey = method.toString() + "@" + context.toString(); IR result = cache.get(hashKey); if (result == null) { result = makeIR(method, context); if (result == null) { Assertions.UNREACHABLE("Unexpected method " + node); } cache.put(hashKey, result); } return result; } private IR makeIR(IMethod method, JavaTypeContext context) { Map constants = HashMapFactory.make(); if (method.getReference().equals(GET_CONSTRUCTOR)) { SSAInstruction instrs[] = makeGetCtorStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_CONSTRUCTORS)) { SSAInstruction instrs[] = makeGetCtorsStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_METHOD)) { SSAInstruction instrs[] = makeGetMethodStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_METHODS)) { SSAInstruction instrs[] = makeGetMethodsStatments(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_CONSTRUCTOR)) { SSAInstruction instrs[] = makeGetDeclCtorStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_CONSTRUCTORS)) { SSAInstruction instrs[] = makeGetDeclCtorsStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_METHOD)) { SSAInstruction instrs[] = makeGetDeclaredMethodStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_METHODS)) { SSAInstruction instrs[] = makeGetDeclaredMethodsStatements(context, constants); return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } Assertions.UNREACHABLE("Unexpected method " + method); return null; } /** END Custom change: caching */ /* * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getNumberOfStatements(com.ibm.wala.ipa.callgraph.CGNode) */ public int getNumberOfStatements(CGNode node) { assert understands(node); return getIR(node).getInstructions().length; } /* * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode) */ public boolean understands(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } if (!(node.getContext() instanceof JavaTypeContext)) { return false; } MethodReference mRef = node.getMethod().getReference(); return mRef.equals(GET_CONSTRUCTOR) || mRef.equals(GET_CONSTRUCTORS) || mRef.equals(GET_METHOD) || mRef.equals(GET_METHODS) || mRef.equals(GET_DECLARED_CONSTRUCTOR) || mRef.equals(GET_DECLARED_CONSTRUCTORS) || mRef.equals(GET_DECLARED_METHOD) || mRef.equals(GET_DECLARED_METHODS); } public Iterator iterateNewSites(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } assert understands(node); JavaTypeContext context = (JavaTypeContext) node.getContext(); TypeReference tr = context.getType().getTypeReference(); if (tr != null) { return new NonNullSingletonIterator(NewSiteReference.make(0, tr)); } return EmptyIterator.instance(); } public Iterator iterateCallSites(CGNode node) { assert understands(node); return EmptyIterator.instance(); } /** * Get all non-constructor, non-class-initializer methods declared by a class */ private Collection getDeclaredNormalMethods(IClass cls) { Collection result = HashSetFactory.make(); for (IMethod m : cls.getDeclaredMethods()) { if (!m.isInit() && !m.isClinit()) { result.add(m); } } return result; } /** * Get all non-constructor, non-class-initializer methods declared by a class and all its superclasses */ private Collection getAllNormalPublicMethods(IClass cls) { Collection result = HashSetFactory.make(); Collection allMethods = null; allMethods = cls.getAllMethods(); for (IMethod m : allMethods) { if (!m.isInit() && !m.isClinit() && m.isPublic()) { result.add(m); } } return result; } /** * Get all the constructors of a class */ private Collection getConstructors(IClass cls) { Collection result = HashSetFactory.make(); for (IMethod m : cls.getDeclaredMethods()) { if (m.isInit()) { result.add(m); } } return result; } /** * Get all the public constructors of a class */ private Collection getPublicConstructors(IClass cls) { Collection result = HashSetFactory.make(); for (IMethod m : cls.getDeclaredMethods()) { if (m.isInit() && m.isPublic()) { result.add(m); } } return result; } /** * create statements for methods like getConstructors() and getMethods(), which return an array of methods. * * @param returnValues the possible return values for this method. */ private SSAInstruction[] getMethodArrayStatements(MethodReference ref, Collection returnValues, JavaTypeContext context, Map constants) { ArrayList statements = new ArrayList(); int nextLocal = ref.getNumberOfParameters() + 2; int retValue = nextLocal++; IClass cls = context.getType().getType(); SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory(); if (cls != null) { TypeReference arrType = ref.getReturnType(); NewSiteReference site = new NewSiteReference(retValue, arrType); int sizeVn = nextLocal++; constants.put(sizeVn, new ConstantValue(returnValues.size())); SSANewInstruction allocArr = insts.NewInstruction(statements.size(), retValue, site, new int[] { sizeVn }); statements.add(allocArr); int i = 0; for (IMethod m : returnValues) { int c = nextLocal++; constants.put(c, new ConstantValue(m)); int index = i++; int indexVn = nextLocal++; constants.put(indexVn, new ConstantValue(index)); SSAArrayStoreInstruction store = insts .ArrayStoreInstruction(statements.size(), retValue, indexVn, c, TypeReference.JavaLangReflectConstructor); statements.add(store); } SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), retValue, false); statements.add(R); } else { // SJF: This is incorrect. TODO: fix and enable. // SSAThrowInstruction t = insts.ThrowInstruction(retValue); // statements.add(t); } SSAInstruction[] result = new SSAInstruction[statements.size()]; Iterator it = statements.iterator(); for (int i = 0; i < result.length; i++) { result[i] = it.next(); } return result; } /** * create statements for methods like getConstructor() and getDeclaredMethod(), which return a single method. This creates a * return statement for each possible return value, each of which is a {@link ConstantValue} for an {@link IMethod}. * * @param returnValues the possible return values for this method. */ private SSAInstruction[] getParticularMethodStatements(MethodReference ref, Collection returnValues, JavaTypeContext context, Map constants) { ArrayList statements = new ArrayList(); int nextLocal = ref.getNumberOfParameters() + 2; IClass cls = context.getType().getType(); SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory(); if (cls != null) { for (IMethod m : returnValues) { int c = nextLocal++; constants.put(c, new ConstantValue(m)); SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), c, false); statements.add(R); } } else { // SJF: This is incorrect. TODO: fix and enable. // SSAThrowInstruction t = insts.ThrowInstruction(retValue); // statements.add(t); } SSAInstruction[] result = new SSAInstruction[statements.size()]; Iterator it = statements.iterator(); for (int i = 0; i < result.length; i++) { result[i] = it.next(); } return result; } /** * create statements for getConstructor() */ private SSAInstruction[] makeGetCtorStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_CONSTRUCTOR, null, context, constants); } else { return getParticularMethodStatements(GET_CONSTRUCTOR, getPublicConstructors(cls), context, constants); } } // TODO private SSAInstruction[] makeGetCtorsStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, null, context, constants); } else { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, getPublicConstructors(cls), context, constants); } } private SSAInstruction[] makeGetMethodStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_METHOD, null, context, constants); } else { return getParticularMethodStatements(GET_METHOD, getAllNormalPublicMethods(cls), context, constants); } } private SSAInstruction[] makeGetMethodsStatments(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_METHODS, null, context, constants); } else { return getMethodArrayStatements(GET_METHODS, getAllNormalPublicMethods(cls), context, constants); } } /** * create statements for getConstructor() */ private SSAInstruction[] makeGetDeclCtorStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_DECLARED_CONSTRUCTOR, null, context, constants); } else { return getParticularMethodStatements(GET_DECLARED_CONSTRUCTOR, getConstructors(cls), context, constants); } } private SSAInstruction[] makeGetDeclCtorsStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, null, context, constants); } else { return getMethodArrayStatements(GET_DECLARED_CONSTRUCTORS, getConstructors(cls), context, constants); } } /** * create statements for getDeclaredMethod() */ private SSAInstruction[] makeGetDeclaredMethodStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_DECLARED_METHOD, null, context, constants); } else { return getParticularMethodStatements(GET_DECLARED_METHOD, getDeclaredNormalMethods(cls), context, constants); } } /** * create statements for getDeclaredMethod() */ private SSAInstruction[] makeGetDeclaredMethodsStatements(JavaTypeContext context, Map constants) { IClass cls = context.getType().getType(); if (cls == null) { return getMethodArrayStatements(GET_DECLARED_METHODS, null, context, constants); } else { return getMethodArrayStatements(GET_DECLARED_METHODS, getDeclaredNormalMethods(cls), context, constants); } } public boolean recordFactoryType(CGNode node, IClass klass) { public Iterator iterateFieldsRead(CGNode node) { return EmptyIterator.instance(); } public Iterator iterateFieldsWritten(CGNode node) { return EmptyIterator.instance(); } public ControlFlowGraph getCFG(CGNode N) { return getIR(N).getControlFlowGraph(); } public DefUse getDU(CGNode node) { return new DefUse(getIR(node)); } }
File
JavaLangClassContextInterpreter.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
import com.ibm.wala.ipa.callgraph.propagation.ReceiverInstanceContext;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction.Dispatch;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;

/**
 * An {@link SSAContextInterpreter} specialized to interpret reflective invocations such as Constructor.newInstance and
 * Method.invoke on an {@link IMethod} constant.
 */
public class ReflectiveInvocationInterpreter extends AbstractReflectionInterpreter {

  public final static MethodReference CTOR_NEW_INSTANCE = MethodReference.findOrCreate(TypeReference.JavaLangReflectConstructor,
      "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;");

  public final static MethodReference METHOD_INVOKE = MethodReference.findOrCreate(TypeReference.JavaLangReflectMethod, "invoke",
      "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");

/** BEGIN Custom change: caching */
  private final Map cache = HashMapFactory.make();
  
/** END Custom change: caching */
  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getIR(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
    ReceiverInstanceContext recv = (ReceiverInstanceContext) node.getContext();
    ConstantKey c = (ConstantKey) recv.getReceiver();
    IMethod m = (IMethod) c.getValue();
/** BEGIN Custom change: caching */
    final IMethod method = node.getMethod();
    final String hashKey = method.toString() + "@" + recv.toString();
    
    IR result = cache.get(hashKey);
    
    if (result == null) {
      result = makeIR(method, m, recv);
      cache.put(hashKey, result);
    }
    
/** END Custom change: caching */
    result = makeIR(node.getMethod(), m, recv);
    return result;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getNumberOfStatements(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (!(node.getContext() instanceof ReceiverInstanceContext)) {
      return false;
    }
    ReceiverInstanceContext r = (ReceiverInstanceContext) node.getContext();
    if (!(r.getReceiver() instanceof ConstantKey)) {
      return false;
    }
    return node.getMethod().getReference().equals(METHOD_INVOKE) || node.getMethod().getReference().equals(CTOR_NEW_INSTANCE);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateNewSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    return getIR(node).iterateNewSites();
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateCallSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return getIR(node).iterateCallSites();
  }

  /**
    m.allInstructions. toArray(instrs);
   * TODO: clean this up. Create the IR for the synthetic method (e.g. Method.invoke)
   * 
   * @param method is something like Method.invoke or Construction.newInstance
   * @param target is the method being called reflectively
   */
  private IR makeIR(IMethod method, IMethod target, ReceiverInstanceContext context) {
    SSAInstructionFactory insts = method.getDeclaringClass().getClassLoader().getInstructionFactory();

    SpecializedMethod m = new SpecializedMethod(method, method.getDeclaringClass(), method.isStatic(), false);
    Map constants = HashMapFactory.make();

    int nextLocal = method.getNumberOfParameters() + 1; // nextLocal = first free value number

    int nargs = target.getNumberOfParameters(); // nargs := number of parameters to target, including "this" pointer
    int args[] = new int[nargs];
    int pc = 0;
    int parametersVn = -1; // parametersVn will hold the value number of parameters array

    if (method.getReference().equals(CTOR_NEW_INSTANCE)) {
      // allocate the new object constructed
      TypeReference allocatedType = target.getDeclaringClass().getReference();
      m
          .addInstruction(allocatedType, insts.NewInstruction(m.allInstructions.size(), args[0] = nextLocal++, NewSiteReference.make(pc++, allocatedType)),
              true);
      parametersVn = 2;
    } else {
      // for Method.invoke, v3 is the parameter to the method being called
      parametersVn = 3;
      if (target.isStatic()) {
        // do nothing
      } else {
        // set up args[0] == the receiver for method.invoke, held in v2.
        // insert a cast for v2 to filter out bogus types
        args[0] = nextLocal++;
        TypeReference type = target.getParameterType(0);
        SSACheckCastInstruction cast = insts.CheckCastInstruction(m.allInstructions.size(), args[0], 2, type, true);
        m.addInstruction(null, cast, false);
      }
    }
    int nextArg = target.isStatic() ? 0 : 1; // nextArg := next index in args[] array that needs to be initialized
    int nextParameter = 0; // nextParameter := next index in the parameters[] array that needs to be copied into the args[] array.

    // load each of the parameters into a local variable, args[something]
    for (int j = nextArg; j < nargs; j++) {
      // load the next parameter into v_temp.
      int indexConst = nextLocal++;
      constants.put(new Integer(indexConst), new ConstantValue(nextParameter++));
      int temp = nextLocal++;
      m.addInstruction(null, insts.ArrayLoadInstruction(m.allInstructions.size(), temp, parametersVn, indexConst, TypeReference.JavaLangObject), false);
      pc++;

      // cast v_temp to the appropriate type and store it in args[j]
      args[j] = nextLocal++;
      TypeReference type = target.getParameterType(j);
      // we insert a cast to filter out bogus types
      SSACheckCastInstruction cast = insts.CheckCastInstruction(m.allInstructions.size(), args[j], temp, type, true);
      m.addInstruction(null, cast, false);
      pc++;
    }

    int exceptions = nextLocal++;
    int result = -1;

    // emit the dispatch and return instructions
    if (method.getReference().equals(CTOR_NEW_INSTANCE)) {
      m.addInstruction(null, insts.InvokeInstruction(m.allInstructions.size(), args, exceptions, CallSiteReference.make(pc++, target.getReference(),
          IInvokeInstruction.Dispatch.SPECIAL)), false);
      m.addInstruction(null, insts.ReturnInstruction(m.allInstructions.size(), args[0], false), false);
    } else {
      Dispatch d = target.isStatic() ? Dispatch.STATIC : Dispatch.VIRTUAL;
      if (target.getReturnType().equals(TypeReference.Void)) {
        m.addInstruction(null, insts.InvokeInstruction(m.allInstructions.size(), args, exceptions, CallSiteReference.make(pc++, target.getReference(), d)),
            false);
      } else {
        result = nextLocal++;
        m.addInstruction(null, insts.InvokeInstruction(m.allInstructions.size(), result, args, exceptions, CallSiteReference.make(pc++,
            target.getReference(), d)), false);
        m.addInstruction(null, insts.ReturnInstruction(m.allInstructions.size(), result, false), false);
      }
    }

    SSAInstruction[] instrs = new SSAInstruction[m.allInstructions.size()];

    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants);
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
import com.ibm.wala.ipa.callgraph.propagation.ReceiverInstanceContext;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction.Dispatch;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;

/**
 * An {@link SSAContextInterpreter} specialized to interpret reflective invocations such as Constructor.newInstance and
 * Method.invoke on an {@link IMethod} constant.
 */
public class ReflectiveInvocationInterpreter extends AbstractReflectionInterpreter {

  public final static MethodReference CTOR_NEW_INSTANCE = MethodReference.findOrCreate(TypeReference.JavaLangReflectConstructor,
      "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;");

  public final static MethodReference METHOD_INVOKE = MethodReference.findOrCreate(TypeReference.JavaLangReflectMethod, "invoke",
      "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getIR(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
    ReceiverInstanceContext recv = (ReceiverInstanceContext) node.getContext();
    ConstantKey c = (ConstantKey) recv.getReceiver();
    IMethod m = (IMethod) c.getValue();
    IR result = makeIR(node.getMethod(), m, recv);
    return result;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getNumberOfStatements(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (!(node.getContext() instanceof ReceiverInstanceContext)) {
      return false;
    }
    ReceiverInstanceContext r = (ReceiverInstanceContext) node.getContext();
    if (!(r.getReceiver() instanceof ConstantKey)) {
      return false;
    }
    return node.getMethod().getReference().equals(METHOD_INVOKE) || node.getMethod().getReference().equals(CTOR_NEW_INSTANCE);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateNewSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    return getIR(node).iterateNewSites();
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateCallSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return getIR(node).iterateCallSites();
  }

  /**
   * TODO: clean this up. Create the IR for the synthetic method (e.g. Method.invoke)
   * 
   * @param method is something like Method.invoke or Construction.newInstance
   * @param target is the method being called reflectively
   */
  private IR makeIR(IMethod method, IMethod target, ReceiverInstanceContext context) {
    SSAInstructionFactory insts = method.getDeclaringClass().getClassLoader().getInstructionFactory();

    SpecializedMethod m = new SpecializedMethod(method, method.getDeclaringClass(), method.isStatic(), false);
    Map constants = HashMapFactory.make();

    int nextLocal = method.getNumberOfParameters() + 1; // nextLocal = first free value number

    int nargs = target.getNumberOfParameters(); // nargs := number of parameters to target, including "this" pointer
    int args[] = new int[nargs];
    int pc = 0;
    int parametersVn = -1; // parametersVn will hold the value number of parameters array

    if (method.getReference().equals(CTOR_NEW_INSTANCE)) {
      // allocate the new object constructed
      TypeReference allocatedType = target.getDeclaringClass().getReference();
      m
          .addInstruction(allocatedType, insts.NewInstruction(args[0] = nextLocal++, NewSiteReference.make(pc++, allocatedType)),
              true);
      parametersVn = 2;
    } else {
      // for Method.invoke, v3 is the parameter to the method being called
      parametersVn = 3;
      if (target.isStatic()) {
        // do nothing
      } else {
        // set up args[0] == the receiver for method.invoke, held in v2.
        // insert a cast for v2 to filter out bogus types
        args[0] = nextLocal++;
        TypeReference type = target.getParameterType(0);
        SSACheckCastInstruction cast = insts.CheckCastInstruction(args[0], 2, type, true);
        m.addInstruction(null, cast, false);
      }
    }
    int nextArg = target.isStatic() ? 0 : 1; // nextArg := next index in args[] array that needs to be initialized
    int nextParameter = 0; // nextParameter := next index in the parameters[] array that needs to be copied into the args[] array.

    // load each of the parameters into a local variable, args[something]
    for (int j = nextArg; j < nargs; j++) {
      // load the next parameter into v_temp.
      int indexConst = nextLocal++;
      constants.put(new Integer(indexConst), new ConstantValue(nextParameter++));
      int temp = nextLocal++;
      m.addInstruction(null, insts.ArrayLoadInstruction(temp, parametersVn, indexConst, TypeReference.JavaLangObject), false);
      pc++;

      // cast v_temp to the appropriate type and store it in args[j]
      args[j] = nextLocal++;
      TypeReference type = target.getParameterType(j);
      // we insert a cast to filter out bogus types
      SSACheckCastInstruction cast = insts.CheckCastInstruction(args[j], temp, type, true);
      m.addInstruction(null, cast, false);
      pc++;
    }

    int exceptions = nextLocal++;
    int result = -1;

    // emit the dispatch and return instructions
    if (method.getReference().equals(CTOR_NEW_INSTANCE)) {
      m.addInstruction(null, insts.InvokeInstruction(args, exceptions, CallSiteReference.make(pc++, target.getReference(),
          IInvokeInstruction.Dispatch.SPECIAL)), false);
      m.addInstruction(null, insts.ReturnInstruction(args[0], false), false);
    } else {
      Dispatch d = target.isStatic() ? Dispatch.STATIC : Dispatch.VIRTUAL;
      if (target.getReturnType().equals(TypeReference.Void)) {
        m.addInstruction(null, insts.InvokeInstruction(args, exceptions, CallSiteReference.make(pc++, target.getReference(), d)),
            false);
      } else {
        result = nextLocal++;
        m.addInstruction(null, insts.InvokeInstruction(result, args, exceptions, CallSiteReference.make(pc++,
            target.getReference(), d)), false);
        m.addInstruction(null, insts.ReturnInstruction(result, false), false);
      }
    }

    SSAInstruction[] instrs = new SSAInstruction[m.allInstructions.size()];
    m.allInstructions. toArray(instrs);

    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants);
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2008 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.analysis.reflection;

import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
import com.ibm.wala.ipa.callgraph.propagation.ReceiverInstanceContext;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction.Dispatch;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;

/**
 * An {@link SSAContextInterpreter} specialized to interpret reflective invocations such as Constructor.newInstance and
 * Method.invoke on an {@link IMethod} constant.
 */
public class ReflectiveInvocationInterpreter extends AbstractReflectionInterpreter {

  public final static MethodReference CTOR_NEW_INSTANCE = MethodReference.findOrCreate(TypeReference.JavaLangReflectConstructor,
      "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;");

  public final static MethodReference METHOD_INVOKE = MethodReference.findOrCreate(TypeReference.JavaLangReflectMethod, "invoke",
      "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");

/** BEGIN Custom change: caching */
  private final Map cache = HashMapFactory.make();
  
/** END Custom change: caching */
  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getIR(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public IR getIR(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    if (DEBUG) {
      System.err.println("generating IR for " + node);
    }
    ReceiverInstanceContext recv = (ReceiverInstanceContext) node.getContext();
    ConstantKey c = (ConstantKey) recv.getReceiver();
    IMethod m = (IMethod) c.getValue();
/** BEGIN Custom change: caching */
    final IMethod method = node.getMethod();
    final String hashKey = method.toString() + "@" + recv.toString();
    
    IR result = cache.get(hashKey);
    
    if (result == null) {
      result = makeIR(method, m, recv);
      cache.put(hashKey, result);
    }
    
/** END Custom change: caching */
    result = makeIR(node.getMethod(), m, recv);
    return result;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getNumberOfStatements(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public int getNumberOfStatements(CGNode node) {
    assert understands(node);
    return getIR(node).getInstructions().length;
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public boolean understands(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    if (!(node.getContext() instanceof ReceiverInstanceContext)) {
      return false;
    }
    ReceiverInstanceContext r = (ReceiverInstanceContext) node.getContext();
    if (!(r.getReceiver() instanceof ConstantKey)) {
      return false;
    }
    return node.getMethod().getReference().equals(METHOD_INVOKE) || node.getMethod().getReference().equals(CTOR_NEW_INSTANCE);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateNewSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateNewSites(CGNode node) {
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    assert understands(node);
    return getIR(node).iterateNewSites();
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#iterateCallSites(com.ibm.wala.ipa.callgraph.CGNode)
   */
  public Iterator iterateCallSites(CGNode node) {
    assert understands(node);
    return getIR(node).iterateCallSites();
  }

  /**
   * TODO: clean this up. Create the IR for the synthetic method (e.g. Method.invoke)
   * 
   * @param method is something like Method.invoke or Construction.newInstance
   * @param target is the method being called reflectively
   */
  private IR makeIR(IMethod method, IMethod target, ReceiverInstanceContext context) {
    SSAInstructionFactory insts = method.getDeclaringClass().getClassLoader().getInstructionFactory();

    SpecializedMethod m = new SpecializedMethod(method, method.getDeclaringClass(), method.isStatic(), false);
    Map constants = HashMapFactory.make();

    int nextLocal = method.getNumberOfParameters() + 1; // nextLocal = first free value number

    int nargs = target.getNumberOfParameters(); // nargs := number of parameters to target, including "this" pointer
    int args[] = new int[nargs];
    int pc = 0;
    int parametersVn = -1; // parametersVn will hold the value number of parameters array

    if (method.getReference().equals(CTOR_NEW_INSTANCE)) {
      // allocate the new object constructed
      TypeReference allocatedType = target.getDeclaringClass().getReference();
      m
          .addInstruction(allocatedType, insts.NewInstruction(m.allInstructions.size(), args[0] = nextLocal++, NewSiteReference.make(pc++, allocatedType)),
              true);
      parametersVn = 2;
    } else {
      // for Method.invoke, v3 is the parameter to the method being called
      parametersVn = 3;
      if (target.isStatic()) {
        // do nothing
      } else {
        // set up args[0] == the receiver for method.invoke, held in v2.
        // insert a cast for v2 to filter out bogus types
        args[0] = nextLocal++;
        TypeReference type = target.getParameterType(0);
        SSACheckCastInstruction cast = insts.CheckCastInstruction(m.allInstructions.size(), args[0], 2, type, true);
        m.addInstruction(null, cast, false);
      }
    }
    int nextArg = target.isStatic() ? 0 : 1; // nextArg := next index in args[] array that needs to be initialized
    int nextParameter = 0; // nextParameter := next index in the parameters[] array that needs to be copied into the args[] array.

    // load each of the parameters into a local variable, args[something]
    for (int j = nextArg; j < nargs; j++) {
      // load the next parameter into v_temp.
      int indexConst = nextLocal++;
      constants.put(new Integer(indexConst), new ConstantValue(nextParameter++));
      int temp = nextLocal++;
      m.addInstruction(null, insts.ArrayLoadInstruction(m.allInstructions.size(), temp, parametersVn, indexConst, TypeReference.JavaLangObject), false);
      pc++;

      // cast v_temp to the appropriate type and store it in args[j]
      args[j] = nextLocal++;
      TypeReference type = target.getParameterType(j);
      // we insert a cast to filter out bogus types
      SSACheckCastInstruction cast = insts.CheckCastInstruction(m.allInstructions.size(), args[j], temp, type, true);
      m.addInstruction(null, cast, false);
      pc++;
    }

    int exceptions = nextLocal++;
    int result = -1;

    // emit the dispatch and return instructions
    if (method.getReference().equals(CTOR_NEW_INSTANCE)) {
      m.addInstruction(null, insts.InvokeInstruction(m.allInstructions.size(), args, exceptions, CallSiteReference.make(pc++, target.getReference(),
          IInvokeInstruction.Dispatch.SPECIAL)), false);
      m.addInstruction(null, insts.ReturnInstruction(m.allInstructions.size(), args[0], false), false);
    } else {
      Dispatch d = target.isStatic() ? Dispatch.STATIC : Dispatch.VIRTUAL;
      if (target.getReturnType().equals(TypeReference.Void)) {
        m.addInstruction(null, insts.InvokeInstruction(m.allInstructions.size(), args, exceptions, CallSiteReference.make(pc++, target.getReference(), d)),
            false);
      } else {
        result = nextLocal++;
        m.addInstruction(null, insts.InvokeInstruction(m.allInstructions.size(), result, args, exceptions, CallSiteReference.make(pc++,
            target.getReference(), d)), false);
        m.addInstruction(null, insts.ReturnInstruction(m.allInstructions.size(), result, false), false);
      }
    }

    SSAInstruction[] instrs = new SSAInstruction[m.allInstructions.size()];
    m.allInstructions. toArray(instrs);

    return new SyntheticIR(method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants);
  }

  public boolean recordFactoryType(CGNode node, IClass klass) {
    return false;
  }

  public Iterator iterateFieldsRead(CGNode node) {
    return EmptyIterator.instance();
  }

  public Iterator iterateFieldsWritten(CGNode node) {
    return EmptyIterator.instance();
  }

  public ControlFlowGraph getCFG(CGNode N) {
    return getIR(N).getControlFlowGraph();
  }

  public DefUse getDU(CGNode node) {
    return new DefUse(getIR(node));
  }
}
File
ReflectiveInvocationInterpreter.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
  /**
}
 */
public interface IMethod extends IMember, ContextItem {
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.classLoader;

import com.ibm.wala.ipa.callgraph.ContextItem;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;

/**
 * Basic interface for an object that represents a single Java method for analysis purposes.

  /**
   * Is this method synchronized?
   */
  boolean isSynchronized();

  /**
   * Is this method a class initializer?
   */
  boolean isClinit();

  /**
   * Is this method an object initializer?
   */
  boolean isInit();

  /**
   * Is this method native?
   */
  boolean isNative();

  /**
   * Did someone synthesize this method? (As opposed to reading it from a class file)
   */
  boolean isSynthetic();

  /**
   * Is this method abstract?
   */
  boolean isAbstract();

  /**
   * Is this method private?
   */
  boolean isPrivate();

  /**
   * Is this method protected?
   */
  boolean isProtected();

  /**
   * Is this method public?
   */
  boolean isPublic();

  /**
   * Is this method final?
   */
  boolean isFinal();
  
  /**
   * Is this method a bridge method?  See JLS 3rd Edition 15.12.4.5
   */
  boolean isBridge();
   * @return canonical MethodReference corresponding to this method
   */
  MethodReference getReference();

  /**
   * @return true iff this method has at least one exception handler
   */
  boolean hasExceptionHandler();

  /**
   * By convention, for a non-static method, getParameterType(0) is the this pointer
   */
  TypeReference getParameterType(int i);

  /**
   * @return the name of the return type for this method
   */
  TypeReference getReturnType();

  /**
   * Method getNumberOfParameters. This result includes the "this" pointer if applicable
   */
  int getNumberOfParameters();

  /**
   * @return an array of the exception types declared by the throws clause for this method, or null if there are none
   * @throws InvalidClassFileException
   */
  TypeReference[] getDeclaredExceptions() throws InvalidClassFileException, UnsupportedOperationException;

  /**
   * @return the source line number corresponding to a particular bytecode index, or -1 if the information is not available.
   */
  int getLineNumber(int bcIndex);
/** BEGIN Custom change: precise positions */
  
  public interface SourcePosition extends Comparable {
    int getFirstLine();
    int getLastLine();
    int getFirstCol();
    int getLastCol();
    int getFirstOffset();
    int getLastOffset(); 
  }
  
  SourcePosition getSourcePosition(int instructionIndex) throws InvalidClassFileException;

  SourcePosition getParameterSourcePosition(int paramNum) throws InvalidClassFileException;
/** END Custom change: precise positions */
  
  /**
   * @return the (source code) name of the local variable of a given number at the specified program counter, or null if the
   *         information is not available.
   */
  String getLocalVariableName(int bcIndex, int localNumber);

  /**
   * something like: com.foo.bar.createLargeOrder(IILjava.lang.String;SLjava.sql.Date;)Ljava.lang.Integer;
   */
  public String getSignature();

  /**
   * something like: foo(Ljava/langString;)Ljava/lang/Class;
   */
  public Selector getSelector();

  /**
   * something like: (IILjava.lang.String;SLjava.sql.Date;)Ljava.lang.Integer;
   */
  Descriptor getDescriptor();

  /**
   * @return true iff the local variable table information for this method is available
   */
  boolean hasLocalVariableTable();
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.classLoader;

import com.ibm.wala.ipa.callgraph.ContextItem;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;

/**
 * Basic interface for an object that represents a single Java method for analysis purposes.
 */
public interface IMethod extends IMember, ContextItem {

  /**
   * Is this method synchronized?
   */
  boolean isSynchronized();

  /**
   * Is this method a class initializer?
   */
  boolean isClinit();

  /**
   * Is this method an object initializer?
   */
  boolean isInit();

  /**
   * Is this method native?
   */
  boolean isNative();

  /**
   * Did someone synthesize this method? (As opposed to reading it from a class file)
   */
  boolean isSynthetic();

  /**
   * Is this method abstract?
   */
  boolean isAbstract();

  /**
   * Is this method private?
   */
  boolean isPrivate();

  /**
   * Is this method protected?
   */
  boolean isProtected();

  /**
   * Is this method public?
   */
  boolean isPublic();

  /**
   * Is this method final?
   */
  boolean isFinal();
  
  /**
   * Is this method a bridge method?  See JLS 3rd Edition 15.12.4.5
   */
  boolean isBridge();

  /**
   * @return canonical MethodReference corresponding to this method
   */
  MethodReference getReference();

  /**
   * @return true iff this method has at least one exception handler
   */
  boolean hasExceptionHandler();

  /**
   * By convention, for a non-static method, getParameterType(0) is the this pointer
   */
  TypeReference getParameterType(int i);

  /**
   * @return the name of the return type for this method
   */
  TypeReference getReturnType();

  /**
   * Method getNumberOfParameters. This result includes the "this" pointer if applicable
   */
  int getNumberOfParameters();

  /**
   * @return an array of the exception types declared by the throws clause for this method, or null if there are none
   * @throws InvalidClassFileException
   */
  TypeReference[] getDeclaredExceptions() throws InvalidClassFileException, UnsupportedOperationException;

  /**
   * @return the source line number corresponding to a particular bytecode index, or -1 if the information is not available.
   */
  int getLineNumber(int bcIndex);

  /**
   * @return the (source code) name of the local variable of a given number at the specified program counter, or null if the
   *         information is not available.
   */
  String getLocalVariableName(int bcIndex, int localNumber);

  /**
   * something like: com.foo.bar.createLargeOrder(IILjava.lang.String;SLjava.sql.Date;)Ljava.lang.Integer;
   */
  public String getSignature();

  /**
   * something like: foo(Ljava/langString;)Ljava/lang/Class;
   */
  public Selector getSelector();

  /**
   * something like: (IILjava.lang.String;SLjava.sql.Date;)Ljava.lang.Integer;
   */
  Descriptor getDescriptor();

  /**
   * @return true iff the local variable table information for this method is available
   */
  boolean hasLocalVariableTable();
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
    int getFirstLine();
 * 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.classLoader;

import com.ibm.wala.ipa.callgraph.ContextItem;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;

/**
 * Basic interface for an object that represents a single Java method for analysis purposes.
 */
public interface IMethod extends IMember, ContextItem {

  /**
   * Is this method synchronized?
   */
  boolean isSynchronized();

  /**
   * Is this method a class initializer?
   */
  boolean isClinit();

  /**
   * Is this method an object initializer?
   */
  boolean isInit();

  /**
   * Is this method native?
   */
  boolean isNative();

  /**
   * Did someone synthesize this method? (As opposed to reading it from a class file)
   */
  boolean isSynthetic();

  /**
   * Is this method abstract?
   */
  boolean isAbstract();

  /**
   * Is this method private?
   */
  boolean isPrivate();

  /**
   * Is this method protected?
   */
  boolean isProtected();

  /**
   * Is this method public?
   */
  boolean isPublic();

  /**
   * Is this method final?
   */
  boolean isFinal();
  
  /**
   * Is this method a bridge method?  See JLS 3rd Edition 15.12.4.5
   */
  boolean isBridge();

  /**
   * @return canonical MethodReference corresponding to this method
   */
  MethodReference getReference();

  /**
   * @return true iff this method has at least one exception handler
   */
  boolean hasExceptionHandler();

  /**
   * By convention, for a non-static method, getParameterType(0) is the this pointer
   */
  TypeReference getParameterType(int i);

  /**
   * @return the name of the return type for this method
   */
  TypeReference getReturnType();

  /**
   * Method getNumberOfParameters. This result includes the "this" pointer if applicable
   */
  int getNumberOfParameters();

  /**
   * @return an array of the exception types declared by the throws clause for this method, or null if there are none
   * @throws InvalidClassFileException
   */
  TypeReference[] getDeclaredExceptions() throws InvalidClassFileException, UnsupportedOperationException;

  /**
   * @return the source line number corresponding to a particular bytecode index, or -1 if the information is not available.
   */
  int getLineNumber(int bcIndex);
/** BEGIN Custom change: precise positions */
  
  public interface SourcePosition extends Comparable {
    int getLastLine();
    int getFirstCol();
    int getLastCol();
    int getFirstOffset();
    int getLastOffset(); 
  }
  
  SourcePosition getSourcePosition(int instructionIndex) throws InvalidClassFileException;

  SourcePosition getParameterSourcePosition(int paramNum) throws InvalidClassFileException;
/** END Custom change: precise positions */
  
  /**
   * @return the (source code) name of the local variable of a given number at the specified program counter, or null if the
   *         information is not available.
   */
  String getLocalVariableName(int bcIndex, int localNumber);

  /**
   * something like: com.foo.bar.createLargeOrder(IILjava.lang.String;SLjava.sql.Date;)Ljava.lang.Integer;
   */
  public String getSignature();

  /**
   * something like: foo(Ljava/langString;)Ljava/lang/Class;
   */
  public Selector getSelector();

  /**
   * something like: (IILjava.lang.String;SLjava.sql.Date;)Ljava.lang.Integer;
   */
  Descriptor getDescriptor();

  /**
   * @return true iff the local variable table information for this method is available
   */
  boolean hasLocalVariableTable();
}
File
IMethod.java
Developer's decision
Version 1
Kind of conflict
Comment
Import
Interface declaration
Package declaration
Chunk
Conflicting content
        @Override
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2009 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.classLoader;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

import com.ibm.wala.analysis.typeInference.JavaPrimitiveType;
import com.ibm.wala.analysis.typeInference.PrimitiveType;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.ConstantInstruction;
import com.ibm.wala.shrikeBT.ConstantInstruction.ClassToken;
import com.ibm.wala.shrikeBT.Constants;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IComparisonInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.Instruction;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.SSAAddressOfInstruction;
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSABinaryOpInstruction;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAComparisonInstruction;
import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
import com.ibm.wala.ssa.SSAConversionInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAGotoInstruction;
import com.ibm.wala.ssa.SSAInstanceofInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSALoadIndirectInstruction;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSAMonitorInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSAStoreIndirectInstruction;
import com.ibm.wala.ssa.SSASwitchInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.ssa.SSAUnaryOpInstruction;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.shrike.Exceptions.MethodResolutionFailure;
import com.ibm.wala.util.shrike.ShrikeUtil;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.warnings.Warnings;

/**
 * The implementation of {@link Language} which defines Java semantics.
 *
 */
public class JavaLanguage extends LanguageImpl implements BytecodeLanguage, Constants {

  public static class JavaInstructionFactory implements SSAInstructionFactory {
    public SSAArrayLengthInstruction ArrayLengthInstruction(int iindex, int result, int arrayref) {
      return new SSAArrayLengthInstruction(iindex, result, arrayref) {
        @Override
        public Collection getExceptionTypes() {
          return getNullPointerException();
        }
      };
    }

    public SSAArrayLoadInstruction ArrayLoadInstruction(int iindex, int result, int arrayref, int index, TypeReference declaredType) {
      return new SSAArrayLoadInstruction(iindex, result, arrayref, index, declaredType) {
        public Collection getExceptionTypes() {
          return getArrayAccessExceptions();
        }
      };
    }

    public SSAArrayStoreInstruction ArrayStoreInstruction(int iindex, int arrayref, int index, int value, TypeReference declaredType) {
      return new SSAArrayStoreInstruction(iindex, arrayref, index, value, declaredType) {
        @Override
        public Collection getExceptionTypes() {
          if (typeIsPrimitive()) {
            return getArrayAccessExceptions();
          } else {
            return getAaStoreExceptions();
          }
        }
      };
    }

    public SSABinaryOpInstruction BinaryOpInstruction(int iindex, IBinaryOpInstruction.IOperator operator, boolean overflow, boolean unsigned,
        int result, int val1, int val2, boolean mayBeInteger) {
      assert !overflow;
      assert !unsigned;
      return new SSABinaryOpInstruction(iindex, operator, result, val1, val2, mayBeInteger) {

        @Override
        public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
          return insts.BinaryOpInstruction(iindex, getOperator(), false, false, defs == null || defs.length == 0 ? getDef(0) : defs[0],
              uses == null ? getUse(0) : uses[0], uses == null ? getUse(1) : uses[1], mayBeIntegerOp());
        }

        @Override
        public Collection getExceptionTypes() {
          if (isPEI()) {
            return getArithmeticException();
          } else {
            return Collections.emptySet();
          }
        }
      };
    }

    public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, int[] typeValues, boolean isPEI) {
      throw new UnsupportedOperationException();
    }
       
    public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, TypeReference[] types, boolean isPEI) {
       assert types.length == 1;
       assert isPEI;
      return new SSACheckCastInstruction(iindex, result, val, types, true) {
        @Override
        public Collection getExceptionTypes() {
          return getClassCastException();
        }
      };
    }
     
    public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, int typeValue, boolean isPEI) {
      assert isPEI;
      return CheckCastInstruction(iindex, result, val, new int[]{ typeValue }, true);
    }

    public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, TypeReference type, boolean isPEI) {
      assert isPEI;
      return CheckCastInstruction(iindex, result, val, new TypeReference[]{ type }, true);
    }

    public SSAComparisonInstruction ComparisonInstruction(int iindex, IComparisonInstruction.Operator operator, int result, int val1, int val2) {
      return new SSAComparisonInstruction(iindex, operator, result, val1, val2);
    }

    public SSAConditionalBranchInstruction ConditionalBranchInstruction(int iindex, IConditionalBranchInstruction.IOperator operator,
        TypeReference type, int val1, int val2) {
      return new SSAConditionalBranchInstruction(iindex, operator, type, val1, val2);
    }

    public SSAConversionInstruction ConversionInstruction(int iindex, int result, int val, TypeReference fromType, TypeReference toType,
        boolean overflow) {
      assert !overflow;
      return new SSAConversionInstruction(iindex, result, val, fromType, toType) {
        @Override
        public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
          if (uses != null && uses.length == 0) {
            throw new IllegalArgumentException("(uses != null) and (uses.length == 0)");
          }
          return insts.ConversionInstruction(iindex, defs == null || defs.length == 0 ? getDef(0) : defs[0], uses == null ? getUse(0)

              : uses[0], getFromType(), getToType(), false);
        }
      };
    }

    public SSAGetCaughtExceptionInstruction GetCaughtExceptionInstruction(int iindex, int bbNumber, int exceptionValueNumber) {
      return new SSAGetCaughtExceptionInstruction(iindex, bbNumber, exceptionValueNumber);
    }

    public SSAGetInstruction GetInstruction(int iindex, int result, FieldReference field) {
      return new SSAGetInstruction(iindex, result, field) {
      };
    }

    public SSAGetInstruction GetInstruction(int iindex, int result, int ref, FieldReference field) {
      return new SSAGetInstruction(iindex, result, ref, field) {
        @Override
        public Collection getExceptionTypes() {
          return getNullPointerException();
        }
      };
    }

    public SSAGotoInstruction GotoInstruction(int iindex) {
      return new SSAGotoInstruction(iindex);
    }

    public SSAInstanceofInstruction InstanceofInstruction(int iindex, int result, int ref, TypeReference checkedType) {
      return new SSAInstanceofInstruction(iindex, result, ref, checkedType);
    }

    public SSAInvokeInstruction InvokeInstruction(int iindex, int result, int[] params, int exception, CallSiteReference site) {
      return new SSAInvokeInstruction(iindex, result, params, exception, site) {
        @Override
        public Collection getExceptionTypes() {
          if (!isStatic()) {
            return getNullPointerException();
          } else {
            return Collections.emptySet();
          }
        }
      };
    }

    public SSAInvokeInstruction InvokeInstruction(int iindex, int[] params, int exception, CallSiteReference site) {
      return new SSAInvokeInstruction(iindex, params, exception, site) {
        @Override
        public Collection getExceptionTypes() {
          if (!isStatic()) {
            return getNullPointerException();
          } else {
            return Collections.emptySet();
          }
        }
      };
    }

    public SSAMonitorInstruction MonitorInstruction(int iindex, int ref, boolean isEnter) {
      return new SSAMonitorInstruction(iindex, ref, isEnter) {
        @Override
        public Collection getExceptionTypes() {
          return getNullPointerException();
        }
      };
    }

    public SSANewInstruction NewInstruction(int iindex, int result, NewSiteReference site) {
      return new SSANewInstruction(iindex, result, site) {
        @Override
        public Collection getExceptionTypes() {
          if (getNewSite().getDeclaredType().isArrayType()) {
            return getNewArrayExceptions();
          } else {
            return getNewScalarExceptions();
          }
        }
      };
    }

    public SSAPhiInstruction PhiInstruction(int iindex, int result, int[] params) throws IllegalArgumentException {
      return new SSAPhiInstruction(iindex, result, params) {
      };
    }

    public SSAPutInstruction PutInstruction(int iindex, int ref, int value, FieldReference field) {
      return new SSAPutInstruction(iindex, ref, value, field) {
        @Override
        public Collection getExceptionTypes() {
          return getNullPointerException();
        }
      };
    }

    public SSAPutInstruction PutInstruction(int iindex, int value, FieldReference field) {
      return new SSAPutInstruction(iindex, value, field) {
      };
    }
    public SSAReturnInstruction ReturnInstruction(int iindex) {
      return new SSAReturnInstruction(iindex);
    }

    public SSAReturnInstruction ReturnInstruction(int iindex, int result, boolean isPrimitive) {
      return new SSAReturnInstruction(iindex, result, isPrimitive);
    }

    public SSASwitchInstruction SwitchInstruction(int iindex, int val, int defaultLabel, int[] casesAndLabels) {
      return new SSASwitchInstruction(iindex, val, defaultLabel, casesAndLabels);
    }

    public SSAThrowInstruction ThrowInstruction(int iindex, int exception) {
      return new SSAThrowInstruction(iindex, exception) {
        @Override
        public Collection getExceptionTypes() {
          return getNullPointerException();
        }
      };
    }

    public SSAUnaryOpInstruction UnaryOpInstruction(int iindex, IUnaryOpInstruction.IOperator operator, int result, int val) {
      return new SSAUnaryOpInstruction(iindex, operator, result, val);
    }

    public SSALoadMetadataInstruction LoadMetadataInstruction(int iindex, int lval, TypeReference entityType, Object token) {
      return new SSALoadMetadataInstruction(iindex, lval, entityType, token) {
        @Override
        public Collection getExceptionTypes() {
          return loadClassExceptions;
        }
      };
    }

    public SSANewInstruction NewInstruction(int iindex, int result, NewSiteReference site, int[] params) {
      return new SSANewInstruction(iindex, result, site, params) {
        @Override
        public Collection getExceptionTypes() {
          return getNewArrayExceptions();
        }
      };
    }

    public SSAPiInstruction PiInstruction(int iindex, int result, int val, int piBlock, int successorBlock, SSAInstruction cause) {
      return new SSAPiInstruction(iindex, result, val, piBlock, successorBlock, cause);
    }

    public SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, TypeReference pointeeType) {
      throw new UnsupportedOperationException();
    }

    return TypeReference.JavaLangObject;
    public SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, int indexVal, TypeReference pointeeType) {
      throw new UnsupportedOperationException();
   }

    public SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, FieldReference field, TypeReference pointeeType) {
      throw new UnsupportedOperationException();
    }

    public SSALoadIndirectInstruction LoadIndirectInstruction(int iindex, int lval, TypeReference t, int addressVal) {
      throw new UnsupportedOperationException();
    }

    public SSAStoreIndirectInstruction StoreIndirectInstruction(int iindex, int addressVal, int rval, TypeReference pointeeType) {
      throw new UnsupportedOperationException();
    }
  }

  private static final Collection arrayAccessExceptions = Collections.unmodifiableCollection(Arrays
      .asList(new TypeReference[] { TypeReference.JavaLangNullPointerException,
          TypeReference.JavaLangArrayIndexOutOfBoundsException }));

  private static final Collection aaStoreExceptions = Collections.unmodifiableCollection(Arrays
      .asList(new TypeReference[] { TypeReference.JavaLangNullPointerException,
          TypeReference.JavaLangArrayIndexOutOfBoundsException, TypeReference.JavaLangArrayStoreException }));

  private static final Collection newScalarExceptions = Collections.unmodifiableCollection(Arrays
      .asList(new TypeReference[] { TypeReference.JavaLangExceptionInInitializerError, TypeReference.JavaLangOutOfMemoryError }));

  private static final Collection newArrayExceptions = Collections.unmodifiableCollection(Arrays
      .asList(new TypeReference[] { TypeReference.JavaLangOutOfMemoryError, TypeReference.JavaLangNegativeArraySizeException }));

  private static final Collection exceptionInInitializerError = Collections
      .singleton(TypeReference.JavaLangExceptionInInitializerError);

  private static final Collection nullPointerException = Collections
      .singleton(TypeReference.JavaLangNullPointerException);

  private static final Collection arithmeticException = Collections
      .singleton(TypeReference.JavaLangArithmeticException);

  private static final Collection classCastException = Collections
      .singleton(TypeReference.JavaLangClassCastException);

  private static final Collection classNotFoundException = Collections
      .singleton(TypeReference.JavaLangClassNotFoundException);

  private static final Collection loadClassExceptions = Collections
      .singleton(TypeReference.JavaLangClassNotFoundException);

  public static Collection getAaStoreExceptions() {
    return aaStoreExceptions;
  }

  public static Collection getArithmeticException() {
    return arithmeticException;
  }

  public static Collection getArrayAccessExceptions() {
    return arrayAccessExceptions;
  }

  public static Collection getClassCastException() {
    return classCastException;
  }

  public static Collection getClassNotFoundException() {
    return classNotFoundException;
  }

  public static Collection getNewArrayExceptions() {
    return newArrayExceptions;
  }

  public static Collection getNewScalarExceptions() {
    return newScalarExceptions;
  }

  public static Collection getNullPointerException() {
    return nullPointerException;
  }

  public static Collection getExceptionInInitializerError() {
    return exceptionInInitializerError;
  }

  public Atom getName() {
    return ClassLoaderReference.Java;
  }

  public TypeReference getRootType() {
  }

  public TypeReference getThrowableType() {
    return TypeReference.JavaLangThrowable;
  }

  public TypeReference getConstantType(Object o) {
    if (o == null) {
      // TODO: do we really want null here instead of TypeReference.Null?
      // lots of code seems to depend on this being null.
      return null;
    } else if (o instanceof Boolean) {
      return TypeReference.Boolean;
    } else if (o instanceof Long) {
      return TypeReference.Long;
    } else if (o instanceof Double) {
      return TypeReference.Double;
    } else if (o instanceof Float) {
      return TypeReference.Float;
    } else if (o instanceof Number) {
      return TypeReference.Int;
    } else if (o instanceof String) {
      return TypeReference.JavaLangString;
    } else if (o instanceof ClassToken || o instanceof TypeReference) {
      return TypeReference.JavaLangClass;
    } else if (o instanceof IMethod) {
      IMethod m = (IMethod) o;
      return m.isInit() ? TypeReference.JavaLangReflectConstructor : TypeReference.JavaLangReflectMethod;
    } else {
      assert false : "unknown constant " + o + ": " + o.getClass();
      return null;
    }
  }

  public boolean isNullType(TypeReference type) {
    return type == null || type == TypeReference.Null;
  }

  public TypeReference[] getArrayInterfaces() {
    return new TypeReference[] { TypeReference.JavaIoSerializable, TypeReference.JavaLangCloneable };
  }

  public TypeName lookupPrimitiveType(String name) {
    throw new UnsupportedOperationException();
  }

  /**
   * @return Collection, set of exception types a call to a declared target might throw.
   * @throws InvalidClassFileException
   * @throws IllegalArgumentException if target is null
   * @throws IllegalArgumentException if cha is null
   */
  public Collection inferInvokeExceptions(MethodReference target, IClassHierarchy cha)
      throws InvalidClassFileException {

    if (cha == null) {
      throw new IllegalArgumentException("cha is null");
    }
    if (target == null) {
      throw new IllegalArgumentException("target is null");
    }
    ArrayList set = new ArrayList(cha.getJavaLangRuntimeExceptionTypes());
    set.addAll(cha.getJavaLangErrorTypes());

    IClass klass = cha.lookupClass(target.getDeclaringClass());
    if (klass == null) {
      Warnings.add(MethodResolutionFailure.moderate(target));
    }
    if (klass != null) {
      IMethod M = klass.getMethod(target.getSelector());
      if (M == null) {
        Warnings.add(MethodResolutionFailure.severe(target));
      } else {
        TypeReference[] exceptionTypes = M.getDeclaredExceptions();
        if (exceptionTypes != null) {
          set.addAll(Arrays.asList(exceptionTypes));
        }
      }
    }
    return set;
  }

  /**
   * @param pei a potentially-excepting instruction
   * @return the exception types that pei may throw, independent of the class hierarchy. null if none.
   * 
   *         Notes
   *         
    *
  • this method will NOT return the exception type explicitly thrown by an athrow *
  • this method will NOT return the exception types that a called method may throw *
  • this method ignores OutOfMemoryError *
  • this method ignores linkage errors *
  • this method ignores IllegalMonitorState exceptions *
* * @throws IllegalArgumentException if pei is null */ } public Collection getImplicitExceptionTypes(IInstruction pei) { if (pei == null) { throw new IllegalArgumentException("pei is null"); } switch (((Instruction) pei).getOpcode()) { case OP_iaload: case OP_laload: case OP_faload: case OP_daload: case OP_aaload: case OP_baload: case OP_caload: case OP_saload: case OP_iastore: case OP_lastore: case OP_fastore: case OP_dastore: case OP_bastore: case OP_castore: case OP_sastore: return getArrayAccessExceptions(); case OP_aastore: return getAaStoreExceptions(); case OP_getfield: case OP_putfield: case OP_invokevirtual: case OP_invokespecial: case OP_invokeinterface: return getNullPointerException(); case OP_idiv: case OP_irem: case OP_ldiv: case OP_lrem: return getArithmeticException(); case OP_new: return newScalarExceptions; case OP_newarray: case OP_anewarray: case OP_multianewarray: return newArrayExceptions; case OP_arraylength: return getNullPointerException(); case OP_athrow: // N.B: the caller must handle the explicitly-thrown exception return getNullPointerException(); case OP_checkcast: return getClassCastException(); case OP_monitorenter: case OP_monitorexit: // we're currently ignoring MonitorStateExceptions, since J2EE stuff // should be // logically single-threaded return getNullPointerException(); case OP_ldc_w: if (((ConstantInstruction) pei).getType().equals(TYPE_Class)) return getClassNotFoundException(); else return null; case OP_getstatic: case OP_putstatic: return getExceptionInInitializerError(); default: return Collections.emptySet(); } } public SSAInstructionFactory instructionFactory() { return javaShrikeFactory; } private final static SSAInstructionFactory javaShrikeFactory = new JavaInstructionFactory(); public boolean isDoubleType(TypeReference type) { return type == TypeReference.Double; } public boolean isFloatType(TypeReference type) { return type == TypeReference.Float; } public boolean isIntType(TypeReference type) { return type == TypeReference.Int; } public boolean isLongType(TypeReference type) { return type == TypeReference.Long; } public boolean isVoidType(TypeReference type) { return type == TypeReference.Void; } public boolean isMetadataType(TypeReference type) { return type == TypeReference.JavaLangClass; } public boolean isStringType(TypeReference type) { return type == TypeReference.JavaLangString; } public boolean isBooleanType(TypeReference type) { return type == TypeReference.Boolean; } public boolean isCharType(TypeReference type) { return type == TypeReference.Char; } }; public Object getMetadataToken(Object value) { if (value instanceof ClassToken) { return ShrikeUtil.makeTypeReference(ClassLoaderReference.Primordial, ((ClassToken) value).getTypeName()); } else { assert value instanceof TypeReference; return value; } } public TypeReference getPointerType(TypeReference pointee) throws UnsupportedOperationException { throw new UnsupportedOperationException("Java does not permit explicit pointers"); } public TypeReference getMetadataType() { return TypeReference.JavaLangClass; } public TypeReference getStringType() { return TypeReference.JavaLangString; } { JavaPrimitiveType.init(); } @SuppressWarnings("static-access") public PrimitiveType getPrimitive(TypeReference reference) { return JavaPrimitiveType.getPrimitive(reference); } } ======= /******************************************************************************* * Copyright (c) 2009 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.classLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import com.ibm.wala.analysis.typeInference.JavaPrimitiveType; import com.ibm.wala.analysis.typeInference.PrimitiveType; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.shrikeBT.ConstantInstruction; import com.ibm.wala.shrikeBT.ConstantInstruction.ClassToken; import com.ibm.wala.shrikeBT.Constants; import com.ibm.wala.shrikeBT.IBinaryOpInstruction; import com.ibm.wala.shrikeBT.IComparisonInstruction; import com.ibm.wala.shrikeBT.IConditionalBranchInstruction; import com.ibm.wala.shrikeBT.IInstruction; import com.ibm.wala.shrikeBT.IUnaryOpInstruction; import com.ibm.wala.shrikeBT.Instruction; import com.ibm.wala.shrikeCT.InvalidClassFileException; import com.ibm.wala.ssa.SSAAddressOfInstruction; import com.ibm.wala.ssa.SSAArrayLengthInstruction; import com.ibm.wala.ssa.SSAArrayLoadInstruction; import com.ibm.wala.ssa.SSAArrayStoreInstruction; import com.ibm.wala.ssa.SSABinaryOpInstruction; import com.ibm.wala.ssa.SSACheckCastInstruction; import com.ibm.wala.ssa.SSAComparisonInstruction; import com.ibm.wala.ssa.SSAConditionalBranchInstruction; import com.ibm.wala.ssa.SSAConversionInstruction; import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction; import com.ibm.wala.ssa.SSAGetInstruction; import com.ibm.wala.ssa.SSAGotoInstruction; import com.ibm.wala.ssa.SSAInstanceofInstruction; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSAInstructionFactory; import com.ibm.wala.ssa.SSAInvokeInstruction; import com.ibm.wala.ssa.SSALoadIndirectInstruction; import com.ibm.wala.ssa.SSALoadMetadataInstruction; import com.ibm.wala.ssa.SSAMonitorInstruction; import com.ibm.wala.ssa.SSANewInstruction; import com.ibm.wala.ssa.SSAPhiInstruction; import com.ibm.wala.ssa.SSAPiInstruction; import com.ibm.wala.ssa.SSAPutInstruction; import com.ibm.wala.ssa.SSAReturnInstruction; import com.ibm.wala.ssa.SSAStoreIndirectInstruction; import com.ibm.wala.ssa.SSASwitchInstruction; import com.ibm.wala.ssa.SSAThrowInstruction; import com.ibm.wala.ssa.SSAUnaryOpInstruction; import com.ibm.wala.types.ClassLoaderReference; import com.ibm.wala.types.FieldReference; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeName; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.shrike.Exceptions.MethodResolutionFailure; import com.ibm.wala.util.shrike.ShrikeUtil; import com.ibm.wala.util.strings.Atom; import com.ibm.wala.util.warnings.Warnings; /** * The implementation of {@link Language} which defines Java semantics. * */ public class JavaLanguage extends LanguageImpl implements BytecodeLanguage, Constants { public static class JavaInstructionFactory implements SSAInstructionFactory { public SSAArrayLengthInstruction ArrayLengthInstruction(int result, int arrayref) { return new SSAArrayLengthInstruction(result, arrayref) { @Override public Collection getExceptionTypes() { return getNullPointerException(); } }; } public SSAArrayLoadInstruction ArrayLoadInstruction(int result, int arrayref, int index, TypeReference declaredType) { return new SSAArrayLoadInstruction(result, arrayref, index, declaredType) { @Override public Collection getExceptionTypes() { return getArrayAccessExceptions(); } }; } public SSAArrayStoreInstruction ArrayStoreInstruction(int arrayref, int index, int value, TypeReference declaredType) { return new SSAArrayStoreInstruction(arrayref, index, value, declaredType) { @Override public Collection getExceptionTypes() { if (typeIsPrimitive()) { return getArrayAccessExceptions(); } else { return getAaStoreExceptions(); } } }; } public SSABinaryOpInstruction BinaryOpInstruction(IBinaryOpInstruction.IOperator operator, boolean overflow, boolean unsigned, int result, int val1, int val2, boolean mayBeInteger) { assert !overflow; assert !unsigned; return new SSABinaryOpInstruction(operator, result, val1, val2, mayBeInteger) { @Override public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) { return insts.BinaryOpInstruction(getOperator(), false, false, defs == null || defs.length == 0 ? getDef(0) : defs[0], uses == null ? getUse(0) : uses[0], uses == null ? getUse(1) : uses[1], mayBeIntegerOp()); } @Override public Collection getExceptionTypes() { if (isPEI()) { return getArithmeticException(); } else { return Collections.emptySet(); } } }; } public SSACheckCastInstruction CheckCastInstruction(int result, int val, int[] typeValues, boolean isPEI) { throw new UnsupportedOperationException(); } public SSACheckCastInstruction CheckCastInstruction(int result, int val, TypeReference[] types, boolean isPEI) { assert types.length == 1; assert isPEI; return new SSACheckCastInstruction(result, val, types, true) { @Override public Collection getExceptionTypes() { return getClassCastException(); } }; } public SSACheckCastInstruction CheckCastInstruction(int result, int val, int typeValue, boolean isPEI) { assert isPEI; return CheckCastInstruction(result, val, new int[]{ typeValue }, true); } public SSACheckCastInstruction CheckCastInstruction(int result, int val, TypeReference type, boolean isPEI) { assert isPEI; return CheckCastInstruction(result, val, new TypeReference[]{ type }, true); } return getNewScalarExceptions(); } public SSAComparisonInstruction ComparisonInstruction(IComparisonInstruction.Operator operator, int result, int val1, int val2) { return new SSAComparisonInstruction(operator, result, val1, val2); } public SSAConditionalBranchInstruction ConditionalBranchInstruction(IConditionalBranchInstruction.IOperator operator, TypeReference type, int val1, int val2) { return new SSAConditionalBranchInstruction(operator, type, val1, val2); } public SSAConversionInstruction ConversionInstruction(int result, int val, TypeReference fromType, TypeReference toType, boolean overflow) { assert !overflow; return new SSAConversionInstruction(result, val, fromType, toType) { @Override public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException { if (uses != null && uses.length == 0) { throw new IllegalArgumentException("(uses != null) and (uses.length == 0)"); } return insts.ConversionInstruction(defs == null || defs.length == 0 ? getDef(0) : defs[0], uses == null ? getUse(0) : uses[0], getFromType(), getToType(), false); } }; } public SSAGetCaughtExceptionInstruction GetCaughtExceptionInstruction(int bbNumber, int exceptionValueNumber) { return new SSAGetCaughtExceptionInstruction(bbNumber, exceptionValueNumber); } public SSAGetInstruction GetInstruction(int result, FieldReference field) { return new SSAGetInstruction(result, field) { }; } public SSAGetInstruction GetInstruction(int result, int ref, FieldReference field) { return new SSAGetInstruction(result, ref, field) { @Override public Collection getExceptionTypes() { return getNullPointerException(); } }; } public SSAGotoInstruction GotoInstruction() { return new SSAGotoInstruction(); } public SSAInstanceofInstruction InstanceofInstruction(int result, int ref, TypeReference checkedType) { return new SSAInstanceofInstruction(result, ref, checkedType); } public SSAInvokeInstruction InvokeInstruction(int result, int[] params, int exception, CallSiteReference site) { return new SSAInvokeInstruction(result, params, exception, site) { @Override public Collection getExceptionTypes() { if (!isStatic()) { return getNullPointerException(); } else { return Collections.emptySet(); } } }; } public SSAInvokeInstruction InvokeInstruction(int[] params, int exception, CallSiteReference site) { return new SSAInvokeInstruction(params, exception, site) { @Override public Collection getExceptionTypes() { if (!isStatic()) { return getNullPointerException(); } else { return Collections.emptySet(); } } }; } public SSAMonitorInstruction MonitorInstruction(int ref, boolean isEnter) { return new SSAMonitorInstruction(ref, isEnter) { @Override public Collection getExceptionTypes() { return getNullPointerException(); } }; } public SSANewInstruction NewInstruction(int result, NewSiteReference site) { return new SSANewInstruction(result, site) { @Override public Collection getExceptionTypes() { if (getNewSite().getDeclaredType().isArrayType()) { return getNewArrayExceptions(); } else { } public SSAPhiInstruction PhiInstruction(int result, int[] params) throws IllegalArgumentException { return new SSAPhiInstruction(result, params) { }; } public SSAPutInstruction PutInstruction(int ref, int value, FieldReference field) { return new SSAPutInstruction(ref, value, field) { @Override public Collection getExceptionTypes() { return getNullPointerException(); } }; } public SSAPutInstruction PutInstruction(int value, FieldReference field) { return new SSAPutInstruction(value, field) { }; } public SSAReturnInstruction ReturnInstruction() { return new SSAReturnInstruction(); } public SSAReturnInstruction ReturnInstruction(int result, boolean isPrimitive) { return new SSAReturnInstruction(result, isPrimitive); } public SSASwitchInstruction SwitchInstruction(int val, int defaultLabel, int[] casesAndLabels) { return new SSASwitchInstruction(val, defaultLabel, casesAndLabels); } public SSAThrowInstruction ThrowInstruction(int exception) { return new SSAThrowInstruction(exception) { @Override public Collection getExceptionTypes() { return getNullPointerException(); } }; } } public SSAUnaryOpInstruction UnaryOpInstruction(IUnaryOpInstruction.IOperator operator, int result, int val) { return new SSAUnaryOpInstruction(operator, result, val); } public SSALoadMetadataInstruction LoadMetadataInstruction(int lval, TypeReference entityType, Object token) { return new SSALoadMetadataInstruction(lval, entityType, token) { @Override public Collection getExceptionTypes() { return loadClassExceptions; } }; } public SSANewInstruction NewInstruction(int result, NewSiteReference site, int[] params) { return new SSANewInstruction(result, site, params) { @Override public Collection getExceptionTypes() { return getNewArrayExceptions(); } }; } public SSAPiInstruction PiInstruction(int result, int val, int piBlock, int successorBlock, SSAInstruction cause) { return new SSAPiInstruction(result, val, piBlock, successorBlock, cause); } public SSAAddressOfInstruction AddressOfInstruction(int lval, int local, TypeReference pointeeType) { throw new UnsupportedOperationException(); } public SSAAddressOfInstruction AddressOfInstruction(int lval, int local, int indexVal, TypeReference pointeeType) { throw new UnsupportedOperationException(); } public SSAAddressOfInstruction AddressOfInstruction(int lval, int local, FieldReference field, TypeReference pointeeType) { throw new UnsupportedOperationException(); } public SSALoadIndirectInstruction LoadIndirectInstruction(int lval, TypeReference t, int addressVal) { throw new UnsupportedOperationException(); } public SSAStoreIndirectInstruction StoreIndirectInstruction(int addressVal, int rval, TypeReference pointeeType) { throw new UnsupportedOperationException(); } } private static final Collection arrayAccessExceptions = Collections.unmodifiableCollection(Arrays .asList(new TypeReference[] { TypeReference.JavaLangNullPointerException, TypeReference.JavaLangArrayIndexOutOfBoundsException })); private static final Collection aaStoreExceptions = Collections.unmodifiableCollection(Arrays .asList(new TypeReference[] { TypeReference.JavaLangNullPointerException, TypeReference.JavaLangArrayIndexOutOfBoundsException, TypeReference.JavaLangArrayStoreException })); private static final Collection newScalarExceptions = Collections.unmodifiableCollection(Arrays .asList(new TypeReference[] { TypeReference.JavaLangExceptionInInitializerError, TypeReference.JavaLangOutOfMemoryError })); private static final Collection newArrayExceptions = Collections.unmodifiableCollection(Arrays .asList(new TypeReference[] { TypeReference.JavaLangOutOfMemoryError, TypeReference.JavaLangNegativeArraySizeException })); private static final Collection exceptionInInitializerError = Collections .singleton(TypeReference.JavaLangExceptionInInitializerError); private static final Collection nullPointerException = Collections .singleton(TypeReference.JavaLangNullPointerException); private static final Collection arithmeticException = Collections .singleton(TypeReference.JavaLangArithmeticException); private static final Collection classCastException = Collections .singleton(TypeReference.JavaLangClassCastException); private static final Collection classNotFoundException = Collections .singleton(TypeReference.JavaLangClassNotFoundException); private static final Collection loadClassExceptions = Collections .singleton(TypeReference.JavaLangClassNotFoundException); public static Collection getAaStoreExceptions() { return aaStoreExceptions; } public static Collection getArithmeticException() { return arithmeticException; public static Collection getArrayAccessExceptions() { return arrayAccessExceptions; } public static Collection getClassCastException() { return classCastException; } public static Collection getClassNotFoundException() { return classNotFoundException; } public static Collection getNewArrayExceptions() { return newArrayExceptions; } public static Collection getNewScalarExceptions() { return newScalarExceptions; } public static Collection getNullPointerException() { return nullPointerException; } public static Collection getExceptionInInitializerError() { return exceptionInInitializerError; } public Atom getName() { return ClassLoaderReference.Java; } public TypeReference getRootType() { return TypeReference.JavaLangObject; } public TypeReference getThrowableType() { return TypeReference.JavaLangThrowable; } public TypeReference getConstantType(Object o) { if (o == null) { // TODO: do we really want null here instead of TypeReference.Null? // lots of code seems to depend on this being null. return null; } else if (o instanceof Boolean) { return TypeReference.Boolean; } else if (o instanceof Long) { return TypeReference.Long; } else if (o instanceof Double) { return TypeReference.Double; } else if (o instanceof Float) { return TypeReference.Float; } else if (o instanceof Number) { return TypeReference.Int; } else if (o instanceof String) { return TypeReference.JavaLangString; } else if (o instanceof ClassToken || o instanceof TypeReference) { return TypeReference.JavaLangClass; } else if (o instanceof IMethod) { IMethod m = (IMethod) o; return m.isInit() ? TypeReference.JavaLangReflectConstructor : TypeReference.JavaLangReflectMethod; } else { assert false : "unknown constant " + o + ": " + o.getClass(); return null; } } public boolean isNullType(TypeReference type) { return type == null || type == TypeReference.Null; } public TypeReference[] getArrayInterfaces() { return new TypeReference[] { TypeReference.JavaIoSerializable, TypeReference.JavaLangCloneable }; } public TypeName lookupPrimitiveType(String name) { throw new UnsupportedOperationException(); } /** * @return Collection, set of exception types a call to a declared target might throw. * @throws InvalidClassFileException * @throws IllegalArgumentException if target is null * @throws IllegalArgumentException if cha is null */ public Collection inferInvokeExceptions(MethodReference target, IClassHierarchy cha) throws InvalidClassFileException { if (cha == null) { throw new IllegalArgumentException("cha is null"); } if (target == null) { throw new IllegalArgumentException("target is null"); } ArrayList set = new ArrayList(cha.getJavaLangRuntimeExceptionTypes()); set.addAll(cha.getJavaLangErrorTypes()); IClass klass = cha.lookupClass(target.getDeclaringClass()); if (klass == null) { return type == TypeReference.Double; Warnings.add(MethodResolutionFailure.moderate(target)); } if (klass != null) { IMethod M = klass.getMethod(target.getSelector()); if (M == null) { Warnings.add(MethodResolutionFailure.severe(target)); } else { TypeReference[] exceptionTypes = M.getDeclaredExceptions(); if (exceptionTypes != null) { set.addAll(Arrays.asList(exceptionTypes)); } } } return set; } /** * @param pei a potentially-excepting instruction * @return the exception types that pei may throw, independent of the class hierarchy. null if none. * * Notes *
    *
  • this method will NOT return the exception type explicitly thrown by an athrow *
  • this method will NOT return the exception types that a called method may throw *
  • this method ignores OutOfMemoryError *
  • this method ignores linkage errors *
  • this method ignores IllegalMonitorState exceptions *
* * @throws IllegalArgumentException if pei is null */ public Collection getImplicitExceptionTypes(IInstruction pei) { if (pei == null) { throw new IllegalArgumentException("pei is null"); } switch (((Instruction) pei).getOpcode()) { case OP_iaload: case OP_laload: case OP_faload: case OP_daload: case OP_aaload: case OP_baload: case OP_caload: case OP_saload: case OP_iastore: case OP_lastore: case OP_fastore: case OP_dastore: case OP_bastore: case OP_castore: case OP_sastore: return getArrayAccessExceptions(); case OP_aastore: return getAaStoreExceptions(); case OP_getfield: case OP_putfield: case OP_invokevirtual: case OP_invokespecial: case OP_invokeinterface: return getNullPointerException(); case OP_idiv: case OP_irem: case OP_ldiv: case OP_lrem: return getArithmeticException(); case OP_new: return newScalarExceptions; case OP_newarray: case OP_anewarray: case OP_multianewarray: return newArrayExceptions; case OP_arraylength: return getNullPointerException(); case OP_athrow: // N.B: the caller must handle the explicitly-thrown exception return getNullPointerException(); case OP_checkcast: return getClassCastException(); case OP_monitorenter: case OP_monitorexit: // we're currently ignoring MonitorStateExceptions, since J2EE stuff // should be // logically single-threaded return getNullPointerException(); case OP_ldc_w: if (((ConstantInstruction) pei).getType().equals(TYPE_Class)) return getClassNotFoundException(); else return null; case OP_getstatic: case OP_putstatic: return getExceptionInInitializerError(); default: return Collections.emptySet(); } } public SSAInstructionFactory instructionFactory() { return javaShrikeFactory; } private final static SSAInstructionFactory javaShrikeFactory = new JavaInstructionFactory(); public boolean isDoubleType(TypeReference type) { } public boolean isFloatType(TypeReference type) { return type == TypeReference.Float; } public boolean isIntType(TypeReference type) { return type == TypeReference.Int; } public boolean isLongType(TypeReference type) { return type == TypeReference.Long; } public boolean isVoidType(TypeReference type) { return type == TypeReference.Void; } public boolean isMetadataType(TypeReference type) { return type == TypeReference.JavaLangClass; } public boolean isStringType(TypeReference type) { return type == TypeReference.JavaLangString; } public boolean isBooleanType(TypeReference type) { return type == TypeReference.Boolean; } public boolean isCharType(TypeReference type) { return type == TypeReference.Char; } public Object getMetadataToken(Object value) { if (value instanceof ClassToken) { return ShrikeUtil.makeTypeReference(ClassLoaderReference.Primordial, ((ClassToken) value).getTypeName()); } else { assert value instanceof TypeReference; return value; } } public TypeReference getPointerType(TypeReference pointee) throws UnsupportedOperationException { throw new UnsupportedOperationException("Java does not permit explicit pointers"); } public TypeReference getMetadataType() { return TypeReference.JavaLangClass; } public TypeReference getStringType() { return TypeReference.JavaLangString; } { JavaPrimitiveType.init(); } @SuppressWarnings("static-access") public PrimitiveType getPrimitive(TypeReference reference) { return JavaPrimitiveType.getPrimitive(reference); } } >>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2009 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.classLoader;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

import com.ibm.wala.analysis.typeInference.JavaPrimitiveType;
import com.ibm.wala.analysis.typeInference.PrimitiveType;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.ConstantInstruction;
import com.ibm.wala.shrikeBT.ConstantInstruction.ClassToken;
import com.ibm.wala.shrikeBT.Constants;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IComparisonInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.Instruction;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.SSAAddressOfInstruction;
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSABinaryOpInstruction;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAComparisonInstruction;
import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
import com.ibm.wala.ssa.SSAConversionInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAGotoInstruction;
import com.ibm.wala.ssa.SSAInstanceofInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSALoadIndirectInstruction;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSAMonitorInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSAStoreIndirectInstruction;
import com.ibm.wala.ssa.SSASwitchInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.ssa.SSAUnaryOpInstruction;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.shrike.Exceptions.MethodResolutionFailure;
import com.ibm.wala.util.shrike.ShrikeUtil;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.warnings.Warnings;

/**
 * The implementation of {@link Language} which defines Java semantics.
 *
 */
public class JavaLanguage extends LanguageImpl implements BytecodeLanguage, Constants {

  public static class JavaInstructionFactory implements SSAInstructionFactory {
    public SSAArrayLengthInstruction ArrayLengthInstruction(int iindex, int result, int arrayref) {
      return new SSAArrayLengthInstruction(iindex, result, arrayref) {
        @Override
        public Collection getExceptionTypes() {
          return getNullPointerException();
        }
      };
    }

    public SSAArrayLoadInstruction ArrayLoadInstruction(int iindex, int result, int arrayref, int index, TypeReference declaredType) {
      return new SSAArrayLoadInstruction(iindex, result, arrayref, index, declaredType) {
        @Override
        public Collection getExceptionTypes() {
          return getArrayAccessExceptions();
        }
      };
    }

    public SSAArrayStoreInstruction ArrayStoreInstruction(int iindex, int arrayref, int index, int value, TypeReference declaredType) {
      return new SSAArrayStoreInstruction(iindex, arrayref, index, value, declaredType) {
        @Override
        public Collection getExceptionTypes() {
          if (typeIsPrimitive()) {
            return getArrayAccessExceptions();
          } else {
            return getAaStoreExceptions();
          }
        }
      };
    }

    public SSABinaryOpInstruction BinaryOpInstruction(int iindex, IBinaryOpInstruction.IOperator operator, boolean overflow, boolean unsigned,
        int result, int val1, int val2, boolean mayBeInteger) {
      assert !overflow;
      assert !unsigned;
      return new SSABinaryOpInstruction(iindex, operator, result, val1, val2, mayBeInteger) {

        @Override
        public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
          return insts.BinaryOpInstruction(iindex, getOperator(), false, false, defs == null || defs.length == 0 ? getDef(0) : defs[0],
              uses == null ? getUse(0) : uses[0], uses == null ? getUse(1) : uses[1], mayBeIntegerOp());
        }

        @Override
        public Collection getExceptionTypes() {
          if (isPEI()) {
            return getArithmeticException();
          } else {
            return Collections.emptySet();
          }
        }
      };
    }

    public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, int[] typeValues, boolean isPEI) {
      throw new UnsupportedOperationException();
    }
       
    public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, TypeReference[] types, boolean isPEI) {
       assert types.length == 1;
       assert isPEI;
      return new SSACheckCastInstruction(iindex, result, val, types, true) {
        @Override
        public Collection getExceptionTypes() {
          return getClassCastException();
        }
      };
    }
     
    public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, int typeValue, boolean isPEI) {
      assert isPEI;
    }
      return CheckCastInstruction(iindex, result, val, new int[]{ typeValue }, true);
    }

    public SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, TypeReference type, boolean isPEI) {
      assert isPEI;
      return CheckCastInstruction(iindex, result, val, new TypeReference[]{ type }, true);
    }

    public SSAComparisonInstruction ComparisonInstruction(int iindex, IComparisonInstruction.Operator operator, int result, int val1, int val2) {
      return new SSAComparisonInstruction(iindex, operator, result, val1, val2);
    }

        }
    public SSAConditionalBranchInstruction ConditionalBranchInstruction(int iindex, IConditionalBranchInstruction.IOperator operator,
        TypeReference type, int val1, int val2) {
      return new SSAConditionalBranchInstruction(iindex, operator, type, val1, val2);
    }

    public SSAConversionInstruction ConversionInstruction(int iindex, int result, int val, TypeReference fromType, TypeReference toType,
        boolean overflow) {
      assert !overflow;
      return new SSAConversionInstruction(iindex, result, val, fromType, toType) {
        @Override
        public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
          if (uses != null && uses.length == 0) {
            throw new IllegalArgumentException("(uses != null) and (uses.length == 0)");
          }
          return insts.ConversionInstruction(iindex, defs == null || defs.length == 0 ? getDef(0) : defs[0], uses == null ? getUse(0)
              : uses[0], getFromType(), getToType(), false);
        }
      };
    }

    public SSAGetCaughtExceptionInstruction GetCaughtExceptionInstruction(int iindex, int bbNumber, int exceptionValueNumber) {
      return new SSAGetCaughtExceptionInstruction(iindex, bbNumber, exceptionValueNumber);
    }

    public SSAGetInstruction GetInstruction(int iindex, int result, FieldReference field) {
      return new SSAGetInstruction(iindex, result, field) {
      };
    }

    public SSAGetInstruction GetInstruction(int iindex, int result, int ref, FieldReference field) {
      return new SSAGetInstruction(iindex, result, ref, field) {
        @Override
        public Collection getExceptionTypes() {
          return getNullPointerException();
        }
      };
    }

    public SSAGotoInstruction GotoInstruction(int iindex) {
      return new SSAGotoInstruction(iindex);
    }

    public SSAInstanceofInstruction InstanceofInstruction(int iindex, int result, int ref, TypeReference checkedType) {
      return new SSAInstanceofInstruction(iindex, result, ref, checkedType);
    }

    public SSAInvokeInstruction InvokeInstruction(int iindex, int result, int[] params, int exception, CallSiteReference site) {
      return new SSAInvokeInstruction(iindex, result, params, exception, site) {
        @Override
        public Collection getExceptionTypes() {
          if (!isStatic()) {
            return getNullPointerException();
          } else {
            return Collections.emptySet();
          }
        }
      };
    }

    public SSAInvokeInstruction InvokeInstruction(int iindex, int[] params, int exception, CallSiteReference site) {
      return new SSAInvokeInstruction(iindex, params, exception, site) {
        @Override
        public Collection getExceptionTypes() {
          if (!isStatic()) {
            return getNullPointerException();
          } else {
            return Collections.emptySet();
          }
        }
      };
    }

    public SSAMonitorInstruction MonitorInstruction(int iindex, int ref, boolean isEnter) {
      return new SSAMonitorInstruction(iindex, ref, isEnter) {
        @Override
        public Collection getExceptionTypes() {

          return getNullPointerException();
        }
      };
    }

    public SSANewInstruction NewInstruction(int iindex, int result, NewSiteReference site) {
      return new SSANewInstruction(iindex, result, site) {
        @Override
        public Collection getExceptionTypes() {
          if (getNewSite().getDeclaredType().isArrayType()) {
            return getNewArrayExceptions();
          } else {
            return getNewScalarExceptions();
          }
      };
    }

    public SSAPhiInstruction PhiInstruction(int iindex, int result, int[] params) throws IllegalArgumentException {
      return new SSAPhiInstruction(iindex, result, params) {
      };
    }

    public SSAPutInstruction PutInstruction(int iindex, int ref, int value, FieldReference field) {
      return new SSAPutInstruction(iindex, ref, value, field) {
        @Override
        public Collection getExceptionTypes() {
          return getNullPointerException();
        }
      };
    }

    public SSAPutInstruction PutInstruction(int iindex, int value, FieldReference field) {
      return new SSAPutInstruction(iindex, value, field) {
      };
    }

    public SSAReturnInstruction ReturnInstruction(int iindex) {
      return new SSAReturnInstruction(iindex);
    }

    public SSAReturnInstruction ReturnInstruction(int iindex, int result, boolean isPrimitive) {
      return new SSAReturnInstruction(iindex, result, isPrimitive);
    }

    public SSASwitchInstruction SwitchInstruction(int iindex, int val, int defaultLabel, int[] casesAndLabels) {
      return new SSASwitchInstruction(iindex, val, defaultLabel, casesAndLabels);
    }

    public SSAThrowInstruction ThrowInstruction(int iindex, int exception) {
      return new SSAThrowInstruction(iindex, exception) {
        @Override
        public Collection getExceptionTypes() {
          return getNullPointerException();
        }
      };
    }

    public SSAUnaryOpInstruction UnaryOpInstruction(int iindex, IUnaryOpInstruction.IOperator operator, int result, int val) {
      return new SSAUnaryOpInstruction(iindex, operator, result, val);
    }

    public SSALoadMetadataInstruction LoadMetadataInstruction(int iindex, int lval, TypeReference entityType, Object token) {
      return new SSALoadMetadataInstruction(iindex, lval, entityType, token) {
        @Override
        public Collection getExceptionTypes() {
          return loadClassExceptions;
        }
      };
    }

    public SSANewInstruction NewInstruction(int iindex, int result, NewSiteReference site, int[] params) {
      return new SSANewInstruction(iindex, result, site, params) {
        @Override
        public Collection getExceptionTypes() {
          return getNewArrayExceptions();
        }
      };
    }

    public SSAPiInstruction PiInstruction(int iindex, int result, int val, int piBlock, int successorBlock, SSAInstruction cause) {
      return new SSAPiInstruction(iindex, result, val, piBlock, successorBlock, cause);
    }

    public SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, TypeReference pointeeType) {
      throw new UnsupportedOperationException();
    }

    public SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, int indexVal, TypeReference pointeeType) {
      throw new UnsupportedOperationException();
   }

    public SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, FieldReference field, TypeReference pointeeType) {
      throw new UnsupportedOperationException();
    public SSALoadIndirectInstruction LoadIndirectInstruction(int iindex, int lval, TypeReference t, int addressVal) {
      throw new UnsupportedOperationException();
    }

    public SSAStoreIndirectInstruction StoreIndirectInstruction(int iindex, int addressVal, int rval, TypeReference pointeeType) {
      throw new UnsupportedOperationException();
    }
  }

  private static final Collection arrayAccessExceptions = Collections.unmodifiableCollection(Arrays
      .asList(new TypeReference[] { TypeReference.JavaLangNullPointerException,
          TypeReference.JavaLangArrayIndexOutOfBoundsException }));

  private static final Collection aaStoreExceptions = Collections.unmodifiableCollection(Arrays
      .asList(new TypeReference[] { TypeReference.JavaLangNullPointerException,
          TypeReference.JavaLangArrayIndexOutOfBoundsException, TypeReference.JavaLangArrayStoreException }));

  private static final Collection newScalarExceptions = Collections.unmodifiableCollection(Arrays
      .asList(new TypeReference[] { TypeReference.JavaLangExceptionInInitializerError, TypeReference.JavaLangOutOfMemoryError }));

  private static final Collection newArrayExceptions = Collections.unmodifiableCollection(Arrays
      .asList(new TypeReference[] { TypeReference.JavaLangOutOfMemoryError, TypeReference.JavaLangNegativeArraySizeException }));

  private static final Collection exceptionInInitializerError = Collections
      .singleton(TypeReference.JavaLangExceptionInInitializerError);

  private static final Collection nullPointerException = Collections
      .singleton(TypeReference.JavaLangNullPointerException);

  private static final Collection arithmeticException = Collections
      .singleton(TypeReference.JavaLangArithmeticException);

  private static final Collection classCastException = Collections
      .singleton(TypeReference.JavaLangClassCastException);

  private static final Collection classNotFoundException = Collections
      .singleton(TypeReference.JavaLangClassNotFoundException);

  private static final Collection loadClassExceptions = Collections
      .singleton(TypeReference.JavaLangClassNotFoundException);

  public static Collection getAaStoreExceptions() {
    return aaStoreExceptions;
  }

  public static Collection getArithmeticException() {
    return arithmeticException;
  }

  public static Collection getArrayAccessExceptions() {
    return arrayAccessExceptions;
  }

  public static Collection getClassCastException() {
    return classCastException;
  }

  public static Collection getClassNotFoundException() {
    return classNotFoundException;
  }

  public static Collection getNewArrayExceptions() {
    return newArrayExceptions;
  }

  public static Collection getNewScalarExceptions() {
    return newScalarExceptions;
  }

  public static Collection getNullPointerException() {
    return nullPointerException;
  }

  public static Collection getExceptionInInitializerError() {
    return exceptionInInitializerError;
  }

  public Atom getName() {
    return ClassLoaderReference.Java;
  }

  public TypeReference getRootType() {
    return TypeReference.JavaLangObject;
  }

  public TypeReference getThrowableType() {
    return TypeReference.JavaLangThrowable;
  }

  public TypeReference getConstantType(Object o) {
    if (o == null) {
      // TODO: do we really want null here instead of TypeReference.Null?
      // lots of code seems to depend on this being null.
      return null;
    } else if (o instanceof Boolean) {
      return TypeReference.Boolean;
    } else if (o instanceof Long) {
      return TypeReference.Long;
    } else if (o instanceof Double) {
      return TypeReference.Double;
    } else if (o instanceof Float) {
      return TypeReference.Float;
    } else if (o instanceof Number) {
      return TypeReference.Int;
    } else if (o instanceof String) {
      return TypeReference.JavaLangString;
    } else if (o instanceof ClassToken || o instanceof TypeReference) {
      return TypeReference.JavaLangClass;
    } else if (o instanceof IMethod) {
      IMethod m = (IMethod) o;
      return m.isInit() ? TypeReference.JavaLangReflectConstructor : TypeReference.JavaLangReflectMethod;
    } else {
      assert false : "unknown constant " + o + ": " + o.getClass();
      return null;
    }
  }

  public boolean isNullType(TypeReference type) {
    return type == null || type == TypeReference.Null;
  }

  public TypeReference[] getArrayInterfaces() {
    return new TypeReference[] { TypeReference.JavaIoSerializable, TypeReference.JavaLangCloneable };
  }

  public TypeName lookupPrimitiveType(String name) {
    throw new UnsupportedOperationException();
  }

  /**
   * @return Collection, set of exception types a call to a declared target might throw.
   * @throws InvalidClassFileException
   * @throws IllegalArgumentException if target is null
   * @throws IllegalArgumentException if cha is null
   */
  public Collection inferInvokeExceptions(MethodReference target, IClassHierarchy cha)
      throws InvalidClassFileException {

    if (cha == null) {
      throw new IllegalArgumentException("cha is null");
    }
    if (target == null) {
      throw new IllegalArgumentException("target is null");
    }
    ArrayList set = new ArrayList(cha.getJavaLangRuntimeExceptionTypes());
    set.addAll(cha.getJavaLangErrorTypes());

    IClass klass = cha.lookupClass(target.getDeclaringClass());
    if (klass == null) {
      Warnings.add(MethodResolutionFailure.moderate(target));
    }
    if (klass != null) {
      IMethod M = klass.getMethod(target.getSelector());
      if (M == null) {
        Warnings.add(MethodResolutionFailure.severe(target));
      } else {
        TypeReference[] exceptionTypes = M.getDeclaredExceptions();
        if (exceptionTypes != null) {
          set.addAll(Arrays.asList(exceptionTypes));
        }
      }
    }
    return set;
  }

  /**
   * @param pei a potentially-excepting instruction
   * @return the exception types that pei may throw, independent of the class hierarchy. null if none.
   * 
   *         Notes
   *         
    *
  • this method will NOT return the exception type explicitly thrown by an athrow *
  • this method will NOT return the exception types that a called method may throw *
  • this method ignores OutOfMemoryError *
  • this method ignores linkage errors *
  • this method ignores IllegalMonitorState exceptions *
* * @throws IllegalArgumentException if pei is null */ public Collection getImplicitExceptionTypes(IInstruction pei) { if (pei == null) { throw new IllegalArgumentException("pei is null"); } switch (((Instruction) pei).getOpcode()) { case OP_iaload: case OP_laload: case OP_faload: case OP_daload: case OP_aaload: case OP_baload: case OP_caload: case OP_saload: case OP_iastore: case OP_lastore: case OP_fastore: case OP_dastore: case OP_bastore: case OP_castore: case OP_sastore: return getArrayAccessExceptions(); case OP_aastore: return getAaStoreExceptions(); case OP_getfield: case OP_putfield: case OP_invokevirtual: case OP_invokespecial: case OP_invokeinterface: return getNullPointerException(); case OP_idiv: case OP_irem: case OP_ldiv: case OP_lrem: return getArithmeticException(); case OP_new: return newScalarExceptions; case OP_newarray: case OP_anewarray: case OP_multianewarray: return newArrayExceptions; case OP_arraylength: return getNullPointerException(); case OP_athrow: // N.B: the caller must handle the explicitly-thrown exception return getNullPointerException(); case OP_checkcast: return getClassCastException(); case OP_monitorenter: case OP_monitorexit: // we're currently ignoring MonitorStateExceptions, since J2EE stuff // should be // logically single-threaded return getNullPointerException(); case OP_ldc_w: if (((ConstantInstruction) pei).getType().equals(TYPE_Class)) return getClassNotFoundException(); else return null; case OP_getstatic: case OP_putstatic: return getExceptionInInitializerError(); default: return Collections.emptySet(); } } public SSAInstructionFactory instructionFactory() { return javaShrikeFactory; } private final static SSAInstructionFactory javaShrikeFactory = new JavaInstructionFactory(); public boolean isDoubleType(TypeReference type) { return type == TypeReference.Double; } public boolean isFloatType(TypeReference type) { return type == TypeReference.Float; } public boolean isIntType(TypeReference type) { return type == TypeReference.Int; } public boolean isLongType(TypeReference type) { return type == TypeReference.Long; } public boolean isVoidType(TypeReference type) { return type == TypeReference.Void; } public boolean isMetadataType(TypeReference type) { return type == TypeReference.JavaLangClass; } public boolean isStringType(TypeReference type) { return type == TypeReference.JavaLangString; } public boolean isBooleanType(TypeReference type) { return type == TypeReference.Boolean; } public boolean isCharType(TypeReference type) { return type == TypeReference.Char; } public Object getMetadataToken(Object value) { if (value instanceof ClassToken) { return ShrikeUtil.makeTypeReference(ClassLoaderReference.Primordial, ((ClassToken) value).getTypeName()); } else { assert value instanceof TypeReference; return value; } } public TypeReference getPointerType(TypeReference pointee) throws UnsupportedOperationException { throw new UnsupportedOperationException("Java does not permit explicit pointers"); } public TypeReference getMetadataType() { return TypeReference.JavaLangClass; } public TypeReference getStringType() { return TypeReference.JavaLangString; } { JavaPrimitiveType.init(); } @SuppressWarnings("static-access") public PrimitiveType getPrimitive(TypeReference reference) { return JavaPrimitiveType.getPrimitive(reference); } }
File
JavaLanguage.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.classLoader;

import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.ibm.wala.shrikeBT.BytecodeConstants;
import com.ibm.wala.shrikeBT.Constants;
import com.ibm.wala.shrikeBT.Decoder;
import com.ibm.wala.shrikeBT.ExceptionHandler;
import com.ibm.wala.shrikeBT.IArrayLoadInstruction;
import com.ibm.wala.shrikeBT.IArrayStoreInstruction;
import com.ibm.wala.shrikeBT.IGetInstruction;
import com.ibm.wala.shrikeBT.IInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeBT.IPutInstruction;
import com.ibm.wala.shrikeBT.ITypeTestInstruction;
import com.ibm.wala.shrikeBT.MonitorInstruction;
import com.ibm.wala.shrikeBT.NewInstruction;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.bytecode.BytecodeStream;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.shrike.ShrikeUtil;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.strings.ImmutableByteArray;

/**
 * A wrapper around a Shrike object that represents a method
 */
public abstract class ShrikeBTMethod implements IMethod, BytecodeConstants {

  /**
   * Some verbose progress output?
   */
  private final static boolean verbose = false;

  private static int methodsParsed = 0;

  /**
   * A wrapper around the declaring class.
   */
  protected final IClass declaringClass;

  /**
   * Canonical reference for this method
   */
  private MethodReference methodReference;

  // break these out to save some space; they're computed lazily.
  protected static class BytecodeInfo {
    Decoder decoder;

    CallSiteReference[] callSites;

    FieldReference[] fieldsWritten;

    FieldReference[] fieldsRead;

    NewSiteReference[] newSites;

    TypeReference[] arraysRead;

    TypeReference[] arraysWritten;

    TypeReference[] implicitExceptions;

    TypeReference[] castTypes;

    boolean hasMonitorOp;

    /**
     * Mapping from instruction index to program counter.
     */
    private int[] pcMap;
/** BEGIN Custom change: precise positions */
    
    /**
     * Cached map representing position information for bytecode instruction
     * at given index
     */
    protected SourcePosition[] positionMap;
    
    /**
     * Sourcecode positions for method parameters
     */
    protected SourcePosition[] paramPositionMap;
    
/** END Custom change: precise positions */
    
    /**
     * Cached map representing line number information in ShrikeCT format TODO: do more careful caching than just soft references
     */
    protected int[] lineNumberMap;

    /**
     * an array mapping bytecode offsets to arrays representing the local variable maps for each offset; a local variable map is
     * represented as an array of localVars*2 elements, containing a pair (nameIndex, typeIndex) for each local variable; a pair
     * (0,0) indicates there is no information for that local variable at that offset
     */
    protected int[][] localVariableMap;

    /**
     * Exception types this method might throw. Computed on demand.
     */
    private TypeReference[] exceptionTypes;
  }

  /**
   * Cache the information about the method statements.
   */
  private SoftReference bcInfo;

  public ShrikeBTMethod(IClass klass) {
    this.declaringClass = klass;
  }

  protected synchronized BytecodeInfo getBCInfo() throws InvalidClassFileException {
    BytecodeInfo result = null;
    if (bcInfo != null) {
      result = bcInfo.get();
    }
    if (result == null) {
      result = computeBCInfo();
      bcInfo = new SoftReference(result);
    }
    return result;
  }

  /**
   * Return the program counter (bytecode index) for a particular Shrike instruction index.
   * 
   * @throws InvalidClassFileException
   */
  public int getBytecodeIndex(int instructionIndex) throws InvalidClassFileException {
    return getBCInfo().pcMap[instructionIndex];
  }

  /**
   * Return the number of Shrike instructions for this method.
   * 
   * @throws InvalidClassFileException
   */
  public int getNumShrikeInstructions() throws InvalidClassFileException {
    return getBCInfo().pcMap.length;
  }

  /**
   * @throws InvalidClassFileException
   */
  public Collection getCallSites() throws InvalidClassFileException {
    Collection empty = Collections.emptySet();
    if (isNative()) {
      return empty;
    }
    return (getBCInfo().callSites == null) ? empty : Collections.unmodifiableCollection(Arrays.asList(getBCInfo().callSites));
  }

  /**
   * @throws InvalidClassFileException
   */
  Collection getNewSites() throws InvalidClassFileException {
    Collection empty = Collections.emptySet();
    if (isNative()) {
      return empty;
    }

    return (getBCInfo().newSites == null) ? empty : Collections.unmodifiableCollection(Arrays.asList(getBCInfo().newSites));
  }

  /**
   * @return Set , the exceptions that statements in this method may throw,
   * @throws InvalidClassFileException
   */
  public Collection getImplicitExceptionTypes() throws InvalidClassFileException {
    if (isNative()) {
      return Collections.emptySet();
    }
    return (getBCInfo().implicitExceptions == null) ? Arrays.asList(new TypeReference[0]) : Arrays
        .asList(getBCInfo().implicitExceptions);
  }

  /**
   * Do a cheap pass over the bytecodes to collect some mapping information. Some methods require this as a pre-req to accessing
   * ShrikeCT information.
   * 
   * @throws InvalidClassFileException
   */
  private BytecodeInfo computeBCInfo() throws InvalidClassFileException {
    BytecodeInfo result = new BytecodeInfo();
    result.exceptionTypes = computeDeclaredExceptions();

    if (isNative()) {
      return result;
    }
    if (verbose) {
      methodsParsed += 1;
      if (methodsParsed % 100 == 0) {
        System.out.println(methodsParsed + " methods processed...");
      }
    }

    processBytecodesWithShrikeBT(result);
    return result;
  }

  /**
   * @return true iff this method has a monitorenter or monitorexit
   * @throws InvalidClassFileException
   */
  public boolean hasMonitorOp() throws InvalidClassFileException {
    if (isNative()) {
      return false;
    }
    return getBCInfo().hasMonitorOp;
  }

  /**
   * @return Set of FieldReference
   * @throws InvalidClassFileException
   */
  public Iterator getFieldsWritten() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
  }
    if (getBCInfo().fieldsWritten == null) {
      return EmptyIterator.instance();
    } else {
      List l = Arrays.asList(getBCInfo().fieldsWritten);
      return l.iterator();
    }
  }

  /**
   * @return Iterator of FieldReference
   * @throws InvalidClassFileException
   */
  public Iterator getFieldsRead() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    if (getBCInfo().fieldsRead == null) {
      return EmptyIterator.instance();
    } else {
      List l = Arrays.asList(getBCInfo().fieldsRead);
      return l.iterator();
    }
  }

  /**
   * @return Iterator of TypeReference
   * @throws InvalidClassFileException
   */
  public Iterator getArraysRead() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    return (getBCInfo().arraysRead == null) ? EmptyIterator.instance() : Arrays.asList(getBCInfo().arraysRead).iterator();
  }

  /**
   * @return Iterator of TypeReference
   * @throws InvalidClassFileException
   */
  public Iterator getArraysWritten() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    if (getBCInfo().fieldsRead == null) {
      return EmptyIterator.instance();
    } else {
      List list = Arrays.asList(getBCInfo().arraysWritten);
      return list.iterator();
    }
  }

  /**
   * @return Iterator of TypeReference
   * @throws InvalidClassFileException
   */
  public Iterator getCastTypes() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
  public String toString() {
    return (getBCInfo().castTypes == null) ? EmptyIterator.instance() : Arrays.asList(getBCInfo().castTypes).iterator();
  }

  protected abstract byte[] getBytecodes();

  /**
   * Method getBytecodeStream.
   * 
   * @return the bytecode stream for this method, or null if no bytecodes.
   */
  public BytecodeStream getBytecodeStream() {
    byte[] bytecodes = getBytecodes();
    if (bytecodes == null) {
      return null;
    } else {
      return new BytecodeStream(this, bytecodes);
    }
  }

  protected abstract String getMethodName() throws InvalidClassFileException;

  protected abstract String getMethodSignature() throws InvalidClassFileException;

  private MethodReference computeMethodReference() {
    try {
      Atom name = Atom.findOrCreateUnicodeAtom(getMethodName());
      ImmutableByteArray desc = ImmutableByteArray.make(getMethodSignature());
      Descriptor D = Descriptor.findOrCreate(declaringClass.getClassLoader().getLanguage(), desc);
      return MethodReference.findOrCreate(declaringClass.getReference(), name, D);
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
      return null;
    }
  }

  public MethodReference getReference() {
    if (methodReference == null) {
      methodReference = computeMethodReference();
    }
    return methodReference;
  }

  public boolean isClinit() {
    return getReference().getSelector().equals(MethodReference.clinitSelector);
  }


  public boolean isInit() {
    return getReference().getName().equals(MethodReference.initAtom);
  }

  protected abstract int getModifiers();

  public boolean isNative() {
    return ((getModifiers() & Constants.ACC_NATIVE) != 0);
  }

  public boolean isAbstract() {
    return ((getModifiers() & Constants.ACC_ABSTRACT) != 0);
  }

  public boolean isPrivate() {
    return ((getModifiers() & Constants.ACC_PRIVATE) != 0);
  }

  public boolean isProtected() {
    return ((getModifiers() & Constants.ACC_PROTECTED) != 0);
  }

  public boolean isPublic() {
    return ((getModifiers() & Constants.ACC_PUBLIC) != 0);
  }

  public boolean isFinal() {
    return ((getModifiers() & Constants.ACC_FINAL) != 0);
  }

  public boolean isBridge() {
    return ((getModifiers() & Constants.ACC_VOLATILE) != 0);
  }

  public boolean isSynchronized() {
    return ((getModifiers() & Constants.ACC_SYNCHRONIZED) != 0);
  }

  public boolean isStatic() {
    return ((getModifiers() & Constants.ACC_STATIC) != 0);
  }

  public boolean isSynthetic() {
    return false;
  }

  public IClass getDeclaringClass() {
    return declaringClass;
  }

  /**
   * Find the decoder object for this method, or create one if necessary.
   * 
   * @return null if the method has no code.
   */
  protected abstract Decoder makeDecoder();

  /**
   * Walk through the bytecodes and collect trivial information.
   * 
   * @throws InvalidClassFileException
   */
  protected abstract void processDebugInfo(BytecodeInfo bcInfo) throws InvalidClassFileException;

  private void processBytecodesWithShrikeBT(BytecodeInfo info) throws InvalidClassFileException {
    info.decoder = makeDecoder();
    if (!isAbstract() && info.decoder == null) {
      Assertions.UNREACHABLE("bad method " + getReference());
    }
    if (info.decoder == null) {
      return;
    }
    info.pcMap = info.decoder.getInstructionsToBytecodes();

    processDebugInfo(info);

    SimpleVisitor simpleVisitor = new SimpleVisitor(info);

    BytecodeLanguage lang = (BytecodeLanguage) getDeclaringClass().getClassLoader().getLanguage();
    IInstruction[] instructions = info.decoder.getInstructions();
    for (int i = 0; i < instructions.length; i++) {
      simpleVisitor.setInstructionIndex(i);
      instructions[i].visit(simpleVisitor);
      if (instructions[i].isPEI()) {
        Collection t = lang.getImplicitExceptionTypes(instructions[i]);
        if (t != null) {
          simpleVisitor.implicitExceptions.addAll(t);
        }
      }
    }

    // copy the Set results into arrays; will use less
    // storage
    copyVisitorSetsToArrays(simpleVisitor, info);
  }

  private void copyVisitorSetsToArrays(SimpleVisitor simpleVisitor, BytecodeInfo info) {
    info.newSites = new NewSiteReference[simpleVisitor.newSites.size()];
    int i = 0;
    for (Iterator it = simpleVisitor.newSites.iterator(); it.hasNext();) {
      info.newSites[i++] = it.next();
    }

    info.fieldsRead = new FieldReference[simpleVisitor.fieldsRead.size()];
    i = 0;
    for (Iterator it = simpleVisitor.fieldsRead.iterator(); it.hasNext();) {
      info.fieldsRead[i++] = it.next();
    }

    info.fieldsRead = new FieldReference[simpleVisitor.fieldsRead.size()];
    i = 0;
    for (Iterator it = simpleVisitor.fieldsRead.iterator(); it.hasNext();) {
      info.fieldsRead[i++] = it.next();
    }

    info.fieldsWritten = new FieldReference[simpleVisitor.fieldsWritten.size()];
    i = 0;
    for (Iterator it = simpleVisitor.fieldsWritten.iterator(); it.hasNext();) {
      info.fieldsWritten[i++] = it.next();
    }

    info.callSites = new CallSiteReference[simpleVisitor.callSites.size()];
    i = 0;
    for (Iterator it = simpleVisitor.callSites.iterator(); it.hasNext();) {
      info.callSites[i++] = it.next();
    }

    info.arraysRead = new TypeReference[simpleVisitor.arraysRead.size()];
    i = 0;
    for (Iterator it = simpleVisitor.arraysRead.iterator(); it.hasNext();) {
      info.arraysRead[i++] = it.next();
    }

    info.arraysWritten = new TypeReference[simpleVisitor.arraysWritten.size()];
    i = 0;
    for (Iterator it = simpleVisitor.arraysWritten.iterator(); it.hasNext();) {
      info.arraysWritten[i++] = it.next();
    }

    info.implicitExceptions = new TypeReference[simpleVisitor.implicitExceptions.size()];
    i = 0;
    for (Iterator it = simpleVisitor.implicitExceptions.iterator(); it.hasNext();) {
      info.implicitExceptions[i++] = (TypeReference) it.next();
    }

    info.castTypes = new TypeReference[simpleVisitor.castTypes.size()];
    i = 0;
    for (Iterator it = simpleVisitor.castTypes.iterator(); it.hasNext();) {
      info.castTypes[i++] = it.next();
    }

    info.hasMonitorOp = simpleVisitor.hasMonitorOp;
  }

  /**
   * @see java.lang.Object#toString()
   */
  @Override
  /*
    return getReference().toString();
  }

  /**
   * @see java.lang.Object#equals(Object)
   */
  @Override
  public boolean equals(Object obj) {
    // instanceof is OK because this class is final.
    // if (this.getClass().equals(obj.getClass())) {
    if (obj instanceof ShrikeBTMethod) {
      ShrikeBTMethod that = (ShrikeBTMethod) obj;
      return (getDeclaringClass().equals(that.getDeclaringClass()) && getReference().equals(that.getReference()));
    } else {
      return false;
    }
  }

  /**
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    return 9661 * getReference().hashCode();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getMaxLocals()
   */
  public abstract int getMaxLocals();

  // TODO: ShrikeBT should have a getMaxStack method on Decoder, I think.
  public abstract int getMaxStackHeight();

  public Atom getName() {
    return getReference().getName();
  }

  public Descriptor getDescriptor() {
    return getReference().getDescriptor();
  }

  /**
   * 
   * A visitor used to process bytecodes
   * 
   */
  private class SimpleVisitor extends IInstruction.Visitor {

    private final BytecodeInfo info;

    public SimpleVisitor(BytecodeInfo info) {
      this.info = info;
    }

    // TODO: make a better Set implementation for these.
    final Set callSites = HashSetFactory.make(5);

    final Set fieldsWritten = HashSetFactory.make(5);

    final Set fieldsRead = HashSetFactory.make(5);

    final Set newSites = HashSetFactory.make(5);

    final Set arraysRead = HashSetFactory.make(5);

    final Set arraysWritten = HashSetFactory.make(5);

    final Set implicitExceptions = HashSetFactory.make(5);

    final Set castTypes = HashSetFactory.make(5);

    boolean hasMonitorOp;

    private int instructionIndex;

    public void setInstructionIndex(int i) {
      instructionIndex = i;
    }

    public int getProgramCounter() throws InvalidClassFileException {
      return info.pcMap[instructionIndex];
    }

    @Override
    public void visitMonitor(MonitorInstruction instruction) {
      hasMonitorOp = true;
    }

    @Override
    public void visitNew(NewInstruction instruction) {
      ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
      TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
      try {
        newSites.add(NewSiteReference.make(getProgramCounter(), t));
      } catch (InvalidClassFileException e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
      }
    }

    @Override
    public void visitGet(IGetInstruction instruction) {
      ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
      FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(), instruction
          .getFieldType());
      fieldsRead.add(f);
    }

    @Override
    public void visitPut(IPutInstruction instruction) {
      ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
      FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(), instruction
          .getFieldType());
      fieldsWritten.add(f);
    }

    @Override
    public void visitInvoke(IInvokeInstruction instruction) {
      IClassLoader loader = getDeclaringClass().getClassLoader();
      MethodReference m = MethodReference.findOrCreate(loader.getLanguage(), loader.getReference(), instruction.getClassType(),
          instruction.getMethodName(), instruction.getMethodSignature());
      int programCounter = 0;
      try {
        programCounter = getProgramCounter();
      } catch (InvalidClassFileException e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
      }
      CallSiteReference site = null;
      site = CallSiteReference.make(programCounter, m, instruction.getInvocationCode());
      callSites.add(site);
    }

    /*
     * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLoad(com.ibm.wala.shrikeBT.ArrayLoadInstruction)
     */
    @Override
    public void visitArrayLoad(IArrayLoadInstruction instruction) {
      arraysRead.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), instruction.getType()));
    }

    /*
     * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayStore(com.ibm.wala.shrikeBT.ArrayStoreInstruction)
     */
    @Override
    public void visitArrayStore(IArrayStoreInstruction instruction) {
      arraysWritten.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), instruction.getType()));
    }

    @Override
    public void visitCheckCast(ITypeTestInstruction instruction) {
      for(String t : instruction.getTypes()) {
        castTypes.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), t));
      }
    }
  }

  /**
   */
  public IInstruction[] getInstructions() throws InvalidClassFileException {
    if (getBCInfo().decoder == null) {
      return null;
    } else {
      return getBCInfo().decoder.getInstructions();
    }
  }

  public ExceptionHandler[][] getHandlers() throws InvalidClassFileException {
    if (getBCInfo().decoder == null) {
      return null;
    } else {
      return getBCInfo().decoder.getHandlers();
    }
  }

  /**
   * By convention, for a non-static method, getParameterType(0) is the this pointer
   */
  public TypeReference getParameterType(int i) {
    if (!isStatic()) {
      if (i == 0) {
        return declaringClass.getReference();
      } else {
        return getReference().getParameterType(i - 1);
      }
    } else {
      return getReference().getParameterType(i);
    }
  }

  /**
   * Method getNumberOfParameters. This result includes the "this" pointer if applicable
   * 
   * @return int
   */
  public int getNumberOfParameters() {
    if (isStatic() || isClinit()) {
      return getReference().getNumberOfParameters();
    } else {
      return getReference().getNumberOfParameters() + 1;
    }
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#hasExceptionHandler()
   */
  @Override
  public abstract boolean hasExceptionHandler();

  /**
   * Clients should not modify the returned array. TODO: clone to avoid the problem?
   * 
   * @throws InvalidClassFileException
   * 
   * @see com.ibm.wala.classLoader.IMethod#getDeclaredExceptions()
   */
  public TypeReference[] getDeclaredExceptions() throws InvalidClassFileException {
    return (getBCInfo().exceptionTypes == null) ? new TypeReference[0] : getBCInfo().exceptionTypes;
  }

  protected abstract String[] getDeclaredExceptionTypeNames() throws InvalidClassFileException;

  /**
   * 
   * @see com.ibm.wala.classLoader.IMethod#getDeclaredExceptions()
   */
  private TypeReference[] computeDeclaredExceptions() {
    try {
      String[] strings = getDeclaredExceptionTypeNames();
      if (strings == null)
        return null;

      ClassLoaderReference loader = getDeclaringClass().getClassLoader().getReference();

      TypeReference[] result = new TypeReference[strings.length];
      for (int i = 0; i < result.length; i++) {
        result[i] = TypeReference.findOrCreate(loader, TypeName.findOrCreate(ImmutableByteArray.make("L" + strings[i])));
      }
      return result;
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
      return null;
    }
  }
/** BEGIN Custom change: precise bytecode positions */
  
  /*
   * @see com.ibm.wala.classLoader.IMethod#getSourcePosition(int)
   */
  public SourcePosition getSourcePosition(int bcIndex) throws InvalidClassFileException {
    return (getBCInfo().positionMap == null) ? null : getBCInfo().positionMap[bcIndex];
  /*
   * @see com.ibm.wala.classLoader.IMethod#getParameterSourcePosition(int)
   */
  public SourcePosition getParameterSourcePosition(int paramNum) throws InvalidClassFileException {
    return (getBCInfo().paramPositionMap == null) ? null : getBCInfo().paramPositionMap[paramNum];
  }
/** END Custom change: precise bytecode positions */

  /*
   * @see com.ibm.wala.classLoader.IMethod#getLineNumber(int)
   */
  public int getLineNumber(int bcIndex) {
    try {
      return (getBCInfo().lineNumberMap == null) ? -1 : getBCInfo().lineNumberMap[bcIndex];
    } catch (InvalidClassFileException e) {
      return -1;
    }
  }

  /**
   * @return Set 
   * @throws InvalidClassFileException
   */
  public Set getCaughtExceptionTypes() throws InvalidClassFileException {

    ExceptionHandler[][] handlers = getHandlers();
    if (handlers == null) {
      return Collections.emptySet();
    }
    HashSet result = HashSetFactory.make(10);
    ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
    for (int i = 0; i < handlers.length; i++) {
      for (int j = 0; j < handlers[i].length; j++) {
        TypeReference t = ShrikeUtil.makeTypeReference(loader, handlers[i][j].getCatchClass());
        if (t == null) {
          t = TypeReference.JavaLangThrowable;
        }
        result.add(t);
      }
    }
    return result;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSignature()
   */
  public String getSignature() {
    return getReference().getSignature();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSelector()
   */
  public Selector getSelector() {
    return getReference().getSelector();
  }

   * @see com.ibm.wala.classLoader.IMethod#getLocalVariableName(int, int)
   */
  public abstract String getLocalVariableName(int bcIndex, int localNumber);

  /*
   * TODO: cache for efficiency?
   * 
   * @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
   */
  public abstract boolean hasLocalVariableTable();

  /**
   * Clear all optional cached data associated with this class.
   */
  public void clearCaches() {
    bcInfo = null;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.classLoader;

import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.ibm.wala.shrikeBT.BytecodeConstants;
import com.ibm.wala.shrikeBT.Constants;
import com.ibm.wala.shrikeBT.Decoder;
import com.ibm.wala.shrikeBT.ExceptionHandler;
import com.ibm.wala.shrikeBT.IArrayLoadInstruction;
import com.ibm.wala.shrikeBT.IArrayStoreInstruction;
import com.ibm.wala.shrikeBT.IGetInstruction;
import com.ibm.wala.shrikeBT.IInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeBT.IPutInstruction;
import com.ibm.wala.shrikeBT.ITypeTestInstruction;
import com.ibm.wala.shrikeBT.MonitorInstruction;
import com.ibm.wala.shrikeBT.NewInstruction;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.bytecode.BytecodeStream;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.shrike.ShrikeUtil;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.strings.ImmutableByteArray;

/**
 * A wrapper around a Shrike object that represents a method
 */
public abstract class ShrikeBTMethod implements IMethod, BytecodeConstants {

  /**
   * Some verbose progress output?
   */
  private final static boolean verbose = false;

  private static int methodsParsed = 0;

  /**
   * A wrapper around the declaring class.
   */
  protected final IClass declaringClass;

  /**
   * Canonical reference for this method
   */
  private MethodReference methodReference;

  // break these out to save some space; they're computed lazily.
  protected static class BytecodeInfo {
    Decoder decoder;

    CallSiteReference[] callSites;

    FieldReference[] fieldsWritten;

    FieldReference[] fieldsRead;


    NewSiteReference[] newSites;

    TypeReference[] arraysRead;

    TypeReference[] arraysWritten;

    TypeReference[] implicitExceptions;

    TypeReference[] castTypes;

    boolean hasMonitorOp;

    /**
     * Mapping from instruction index to program counter.
     */
    private int[] pcMap;

    /**
     * Cached map representing line number information in ShrikeCT format TODO: do more careful caching than just soft references
     */
    protected int[] lineNumberMap;

    /**
     * an array mapping bytecode offsets to arrays representing the local variable maps for each offset; a local variable map is
     * represented as an array of localVars*2 elements, containing a pair (nameIndex, typeIndex) for each local variable; a pair
     * (0,0) indicates there is no information for that local variable at that offset
     */
    protected int[][] localVariableMap;

    /**
     * Exception types this method might throw. Computed on demand.
     */
    private TypeReference[] exceptionTypes;
  }

  /**
   * Cache the information about the method statements.
   */
  private SoftReference bcInfo;

  public ShrikeBTMethod(IClass klass) {
    this.declaringClass = klass;
  }

  protected synchronized BytecodeInfo getBCInfo() throws InvalidClassFileException {
    BytecodeInfo result = null;
    if (bcInfo != null) {
      result = bcInfo.get();
    }
    if (result == null) {
      result = computeBCInfo();
      bcInfo = new SoftReference(result);
    }
    return result;
  }

  /**
   * Return the program counter (bytecode index) for a particular Shrike instruction index.
   * 
   * @throws InvalidClassFileException
   */
  public int getBytecodeIndex(int instructionIndex) throws InvalidClassFileException {
    return getBCInfo().pcMap[instructionIndex];
  }

  /**
   * Return the number of Shrike instructions for this method.
   * 
   * @throws InvalidClassFileException
   */
  public int getNumShrikeInstructions() throws InvalidClassFileException {
    return getBCInfo().pcMap.length;
  }

  /**
   * @throws InvalidClassFileException
   */
  public Collection getCallSites() throws InvalidClassFileException {
    Collection empty = Collections.emptySet();
    if (isNative()) {
      return empty;
    }
    return (getBCInfo().callSites == null) ? empty : Collections.unmodifiableCollection(Arrays.asList(getBCInfo().callSites));
  }

  /**
   * @throws InvalidClassFileException
   */
  Collection getNewSites() throws InvalidClassFileException {
    Collection empty = Collections.emptySet();
    if (isNative()) {
      return empty;
    }

    return (getBCInfo().newSites == null) ? empty : Collections.unmodifiableCollection(Arrays.asList(getBCInfo().newSites));
  }

  /**
   * @return Set , the exceptions that statements in this method may throw,
   * @throws InvalidClassFileException
   */
  public Collection getImplicitExceptionTypes() throws InvalidClassFileException {
    if (isNative()) {
      return Collections.emptySet();
    }
    return (getBCInfo().implicitExceptions == null) ? Arrays.asList(new TypeReference[0]) : Arrays
        .asList(getBCInfo().implicitExceptions);
  }

  /**
   * Do a cheap pass over the bytecodes to collect some mapping information. Some methods require this as a pre-req to accessing
   * ShrikeCT information.
   * 
   * @throws InvalidClassFileException
   */
  private BytecodeInfo computeBCInfo() throws InvalidClassFileException {
    BytecodeInfo result = new BytecodeInfo();
    result.exceptionTypes = computeDeclaredExceptions();

    if (isNative()) {
      return result;
    }
    if (verbose) {
      methodsParsed += 1;
      if (methodsParsed % 100 == 0) {
        System.out.println(methodsParsed + " methods processed...");
      }
    }

    processBytecodesWithShrikeBT(result);
    return result;
  }

  /**
   * @return true iff this method has a monitorenter or monitorexit
   * @throws InvalidClassFileException
   */
  public boolean hasMonitorOp() throws InvalidClassFileException {
    if (isNative()) {
      return false;
    }
    return getBCInfo().hasMonitorOp;
  }

  /**
   * @return Set of FieldReference
   * @throws InvalidClassFileException
   */
  public Iterator getFieldsWritten() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    if (getBCInfo().fieldsWritten == null) {
      this.info = info;
      return EmptyIterator.instance();
    } else {
      List l = Arrays.asList(getBCInfo().fieldsWritten);
      return l.iterator();
    }
  }

  /**
   * @return Iterator of FieldReference
   * @throws InvalidClassFileException
   */
  public Iterator getFieldsRead() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    if (getBCInfo().fieldsRead == null) {
      return EmptyIterator.instance();
    } else {
      List l = Arrays.asList(getBCInfo().fieldsRead);
      return l.iterator();
    }
  }

  /**
   * @return Iterator of TypeReference
   * @throws InvalidClassFileException
   */
  public Iterator getArraysRead() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    return (getBCInfo().arraysRead == null) ? EmptyIterator.instance() : Arrays.asList(getBCInfo().arraysRead).iterator();
  }

  /**
   * @return Iterator of TypeReference
   * @throws InvalidClassFileException
   */
  public Iterator getArraysWritten() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    if (getBCInfo().fieldsRead == null) {
      return EmptyIterator.instance();
    } else {
      List list = Arrays.asList(getBCInfo().arraysWritten);
      return list.iterator();
    }
  }

  /**
   * @return Iterator of TypeReference
   * @throws InvalidClassFileException
   */
  public Iterator getCastTypes() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    @Override
    return (getBCInfo().castTypes == null) ? EmptyIterator.instance() : Arrays.asList(getBCInfo().castTypes).iterator();
  }

  protected abstract byte[] getBytecodes();

  /**
   * Method getBytecodeStream.
   * 
   * @return the bytecode stream for this method, or null if no bytecodes.
   */
  public BytecodeStream getBytecodeStream() {
    byte[] bytecodes = getBytecodes();
    if (bytecodes == null) {
      return null;
    } else {
      return new BytecodeStream(this, bytecodes);
    }
  }

  protected abstract String getMethodName() throws InvalidClassFileException;

  protected abstract String getMethodSignature() throws InvalidClassFileException;

  private MethodReference computeMethodReference() {
    try {
      Atom name = Atom.findOrCreateUnicodeAtom(getMethodName());
      ImmutableByteArray desc = ImmutableByteArray.make(getMethodSignature());
      Descriptor D = Descriptor.findOrCreate(declaringClass.getClassLoader().getLanguage(), desc);
      return MethodReference.findOrCreate(declaringClass.getReference(), name, D);
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
      return null;
    }
  }

  public MethodReference getReference() {
    if (methodReference == null) {
      methodReference = computeMethodReference();
    }
    return methodReference;
  }

  public boolean isClinit() {
    return getReference().getSelector().equals(MethodReference.clinitSelector);
  }

  public boolean isInit() {
    }
    return getReference().getName().equals(MethodReference.initAtom);
  }

  protected abstract int getModifiers();

  public boolean isNative() {
    return ((getModifiers() & Constants.ACC_NATIVE) != 0);
  }

  public boolean isAbstract() {
    return ((getModifiers() & Constants.ACC_ABSTRACT) != 0);
  }

  public boolean isPrivate() {
    return ((getModifiers() & Constants.ACC_PRIVATE) != 0);
  }

  public boolean isProtected() {
    return ((getModifiers() & Constants.ACC_PROTECTED) != 0);
  }

  public boolean isPublic() {
    return ((getModifiers() & Constants.ACC_PUBLIC) != 0);
  }

  public boolean isFinal() {
    return ((getModifiers() & Constants.ACC_FINAL) != 0);
  }

  public boolean isBridge() {
    return ((getModifiers() & Constants.ACC_VOLATILE) != 0);
  }

  public boolean isSynchronized() {
    return ((getModifiers() & Constants.ACC_SYNCHRONIZED) != 0);
  }

  public boolean isStatic() {
    return ((getModifiers() & Constants.ACC_STATIC) != 0);
  }

  public boolean isSynthetic() {
    return false;
  }

  public IClass getDeclaringClass() {
    return declaringClass;
  }

  /**
   * Find the decoder object for this method, or create one if necessary.
   * 
   * @return null if the method has no code.
   */
  protected abstract Decoder makeDecoder();

  /**
   * Walk through the bytecodes and collect trivial information.
   * 
   * @throws InvalidClassFileException
   */
  protected abstract void processDebugInfo(BytecodeInfo bcInfo) throws InvalidClassFileException;

  private void processBytecodesWithShrikeBT(BytecodeInfo info) throws InvalidClassFileException {
    info.decoder = makeDecoder();
    if (!isAbstract() && info.decoder == null) {
      Assertions.UNREACHABLE("bad method " + getReference());
    }
    if (info.decoder == null) {
      return;
    }
    info.pcMap = info.decoder.getInstructionsToBytecodes();

    processDebugInfo(info);

    SimpleVisitor simpleVisitor = new SimpleVisitor(info);

    BytecodeLanguage lang = (BytecodeLanguage) getDeclaringClass().getClassLoader().getLanguage();
    IInstruction[] instructions = info.decoder.getInstructions();
    for (int i = 0; i < instructions.length; i++) {
      simpleVisitor.setInstructionIndex(i);
      instructions[i].visit(simpleVisitor);
      if (instructions[i].isPEI()) {
        Collection t = lang.getImplicitExceptionTypes(instructions[i]);
        if (t != null) {
          simpleVisitor.implicitExceptions.addAll(t);
        }
      }
    }

    // copy the Set results into arrays; will use less
    // storage
    copyVisitorSetsToArrays(simpleVisitor, info);
  }

  private void copyVisitorSetsToArrays(SimpleVisitor simpleVisitor, BytecodeInfo info) {
    info.newSites = new NewSiteReference[simpleVisitor.newSites.size()];
    int i = 0;
    for (Iterator it = simpleVisitor.newSites.iterator(); it.hasNext();) {
      info.newSites[i++] = it.next();
    }

    info.fieldsRead = new FieldReference[simpleVisitor.fieldsRead.size()];
    i = 0;
    public SimpleVisitor(BytecodeInfo info) {
    for (Iterator it = simpleVisitor.fieldsRead.iterator(); it.hasNext();) {
      info.fieldsRead[i++] = it.next();
    }

    info.fieldsRead = new FieldReference[simpleVisitor.fieldsRead.size()];
    i = 0;
    for (Iterator it = simpleVisitor.fieldsRead.iterator(); it.hasNext();) {
      info.fieldsRead[i++] = it.next();
    }

    info.fieldsWritten = new FieldReference[simpleVisitor.fieldsWritten.size()];
    i = 0;
    for (Iterator it = simpleVisitor.fieldsWritten.iterator(); it.hasNext();) {
      info.fieldsWritten[i++] = it.next();
    }

    info.callSites = new CallSiteReference[simpleVisitor.callSites.size()];
    i = 0;
    for (Iterator it = simpleVisitor.callSites.iterator(); it.hasNext();) {
      info.callSites[i++] = it.next();
    }

    info.arraysRead = new TypeReference[simpleVisitor.arraysRead.size()];
    i = 0;
    for (Iterator it = simpleVisitor.arraysRead.iterator(); it.hasNext();) {
      info.arraysRead[i++] = it.next();
    }

    info.arraysWritten = new TypeReference[simpleVisitor.arraysWritten.size()];
    i = 0;
    for (Iterator it = simpleVisitor.arraysWritten.iterator(); it.hasNext();) {
      info.arraysWritten[i++] = it.next();
    }

    info.implicitExceptions = new TypeReference[simpleVisitor.implicitExceptions.size()];
    i = 0;
    for (Iterator it = simpleVisitor.implicitExceptions.iterator(); it.hasNext();) {
      info.implicitExceptions[i++] = (TypeReference) it.next();
    }

    info.castTypes = new TypeReference[simpleVisitor.castTypes.size()];
    i = 0;
    for (Iterator it = simpleVisitor.castTypes.iterator(); it.hasNext();) {
      info.castTypes[i++] = it.next();
    }

    info.hasMonitorOp = simpleVisitor.hasMonitorOp;
  }

  /**
   * @see java.lang.Object#toString()
   */
  public String toString() {
    return getReference().toString();
  }

  /**
   * @see java.lang.Object#equals(Object)
   */
  @Override
  public boolean equals(Object obj) {
    // instanceof is OK because this class is final.
    // if (this.getClass().equals(obj.getClass())) {
    if (obj instanceof ShrikeBTMethod) {
      ShrikeBTMethod that = (ShrikeBTMethod) obj;
      return (getDeclaringClass().equals(that.getDeclaringClass()) && getReference().equals(that.getReference()));
    } else {
      return false;
    }
  }

  /**
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    return 9661 * getReference().hashCode();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getMaxLocals()
   */
  public abstract int getMaxLocals();

  // TODO: ShrikeBT should have a getMaxStack method on Decoder, I think.
  public abstract int getMaxStackHeight();

  public Atom getName() {
    return getReference().getName();
  }

  public Descriptor getDescriptor() {
    return getReference().getDescriptor();
  }

  /**
   * 
   * A visitor used to process bytecodes
   * 
   */
  private class SimpleVisitor extends IInstruction.Visitor {

    private final BytecodeInfo info;


    // TODO: make a better Set implementation for these.
    final Set callSites = HashSetFactory.make(5);

    final Set fieldsWritten = HashSetFactory.make(5);

    final Set fieldsRead = HashSetFactory.make(5);

    final Set newSites = HashSetFactory.make(5);

    final Set arraysRead = HashSetFactory.make(5);

    final Set arraysWritten = HashSetFactory.make(5);

    final Set implicitExceptions = HashSetFactory.make(5);

    final Set castTypes = HashSetFactory.make(5);

    boolean hasMonitorOp;

    private int instructionIndex;

    public void setInstructionIndex(int i) {
      instructionIndex = i;
    }

    public int getProgramCounter() throws InvalidClassFileException {
      return info.pcMap[instructionIndex];
    }

    @Override
    public void visitMonitor(MonitorInstruction instruction) {
      hasMonitorOp = true;
    }

    @Override
    public void visitNew(NewInstruction instruction) {
      ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
      TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
      try {
        newSites.add(NewSiteReference.make(getProgramCounter(), t));
      } catch (InvalidClassFileException e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
      }
    }

    @Override
    public void visitGet(IGetInstruction instruction) {
      ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
      FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(), instruction
          .getFieldType());
      fieldsRead.add(f);
    }
    public void visitPut(IPutInstruction instruction) {
      ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
      FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(), instruction
          .getFieldType());
      fieldsWritten.add(f);
    }

    @Override
    public void visitInvoke(IInvokeInstruction instruction) {
      IClassLoader loader = getDeclaringClass().getClassLoader();
      MethodReference m = MethodReference.findOrCreate(loader.getLanguage(), loader.getReference(), instruction.getClassType(),
          instruction.getMethodName(), instruction.getMethodSignature());
      int programCounter = 0;
      try {
        programCounter = getProgramCounter();
      } catch (InvalidClassFileException e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
      }
      CallSiteReference site = null;
      site = CallSiteReference.make(programCounter, m, instruction.getInvocationCode());
      callSites.add(site);
    }

    /*
     * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLoad(com.ibm.wala.shrikeBT.ArrayLoadInstruction)
     */
    @Override
    public void visitArrayLoad(IArrayLoadInstruction instruction) {
      arraysRead.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), instruction.getType()));
    }

    /*
     * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayStore(com.ibm.wala.shrikeBT.ArrayStoreInstruction)
     */
    @Override
    public void visitArrayStore(IArrayStoreInstruction instruction) {
      arraysWritten.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), instruction.getType()));
    }

    @Override
    public void visitCheckCast(ITypeTestInstruction instruction) {
      for(String t : instruction.getTypes()) {
        castTypes.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), t));
      }
    }
  }

  /**
   */
  public IInstruction[] getInstructions() throws InvalidClassFileException {
    if (getBCInfo().decoder == null) {
      return null;
    } else {
      return getBCInfo().decoder.getInstructions();
    }
  }

  public ExceptionHandler[][] getHandlers() throws InvalidClassFileException {
    if (getBCInfo().decoder == null) {
      return null;
    } else {
      return getBCInfo().decoder.getHandlers();
    }
  }

  /**
   * By convention, for a non-static method, getParameterType(0) is the this pointer
   */
  public TypeReference getParameterType(int i) {
    if (!isStatic()) {
      if (i == 0) {
        return declaringClass.getReference();
      } else {
        return getReference().getParameterType(i - 1);
      }
    } else {
      return getReference().getParameterType(i);
    }
  }

  /**
   * Method getNumberOfParameters. This result includes the "this" pointer if applicable
   * 
   * @return int
   */
  public int getNumberOfParameters() {
    if (isStatic() || isClinit()) {
      return getReference().getNumberOfParameters();
    } else {
      return getReference().getNumberOfParameters() + 1;
    }
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#hasExceptionHandler()
   */
  public abstract boolean hasExceptionHandler();

  /**
   * Clients should not modify the returned array. TODO: clone to avoid the problem?
   * 
   * @throws InvalidClassFileException
   * 
   * @see com.ibm.wala.classLoader.IMethod#getDeclaredExceptions()
   */
  public TypeReference[] getDeclaredExceptions() throws InvalidClassFileException {
    return (getBCInfo().exceptionTypes == null) ? new TypeReference[0] : getBCInfo().exceptionTypes;
  }

  protected abstract String[] getDeclaredExceptionTypeNames() throws InvalidClassFileException;

  /**
   * 
   * @see com.ibm.wala.classLoader.IMethod#getDeclaredExceptions()
   */
  private TypeReference[] computeDeclaredExceptions() {
    try {
      String[] strings = getDeclaredExceptionTypeNames();
      if (strings == null)
        return null;

      ClassLoaderReference loader = getDeclaringClass().getClassLoader().getReference();

      TypeReference[] result = new TypeReference[strings.length];
      for (int i = 0; i < result.length; i++) {
        result[i] = TypeReference.findOrCreate(loader, TypeName.findOrCreate(ImmutableByteArray.make("L" + strings[i])));
      }
      return result;
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
      return null;
    }
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getLineNumber(int)
   */
  public int getLineNumber(int bcIndex) {
    try {
      return (getBCInfo().lineNumberMap == null) ? -1 : getBCInfo().lineNumberMap[bcIndex];
    } catch (InvalidClassFileException e) {
      return -1;
    }
  }

  /**
   * @return Set 
   * @throws InvalidClassFileException
   */
  public Set getCaughtExceptionTypes() throws InvalidClassFileException {

    ExceptionHandler[][] handlers = getHandlers();
    if (handlers == null) {
      return Collections.emptySet();
    }
    HashSet result = HashSetFactory.make(10);
    ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
    for (int i = 0; i < handlers.length; i++) {
      for (int j = 0; j < handlers[i].length; j++) {
        TypeReference t = ShrikeUtil.makeTypeReference(loader, handlers[i][j].getCatchClass());
        if (t == null) {
          t = TypeReference.JavaLangThrowable;
        }
        result.add(t);
      }
    }
    return result;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSignature()
   */
  public String getSignature() {
    return getReference().getSignature();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSelector()
   */
  public Selector getSelector() {
    return getReference().getSelector();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getLocalVariableName(int, int)
   */
  public abstract String getLocalVariableName(int bcIndex, int localNumber);

  /*
   * TODO: cache for efficiency?
   * 
   * @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
   */
  public abstract boolean hasLocalVariableTable();

  /**
   * Clear all optional cached data associated with this class.
   */
  public void clearCaches() {
    bcInfo = null;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.classLoader;

import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.ibm.wala.shrikeBT.BytecodeConstants;
import com.ibm.wala.shrikeBT.Constants;
import com.ibm.wala.shrikeBT.Decoder;
import com.ibm.wala.shrikeBT.ExceptionHandler;
import com.ibm.wala.shrikeBT.IArrayLoadInstruction;
import com.ibm.wala.shrikeBT.IArrayStoreInstruction;
import com.ibm.wala.shrikeBT.IGetInstruction;
import com.ibm.wala.shrikeBT.IInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeBT.IPutInstruction;
import com.ibm.wala.shrikeBT.ITypeTestInstruction;
import com.ibm.wala.shrikeBT.MonitorInstruction;
import com.ibm.wala.shrikeBT.NewInstruction;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.bytecode.BytecodeStream;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.shrike.ShrikeUtil;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.strings.ImmutableByteArray;

/**
 * A wrapper around a Shrike object that represents a method
 */
public abstract class ShrikeBTMethod implements IMethod, BytecodeConstants {

  /**
   * Some verbose progress output?
   */
  private final static boolean verbose = false;

  private static int methodsParsed = 0;

  /**
   * A wrapper around the declaring class.
   */
  protected final IClass declaringClass;

  /**
   * Canonical reference for this method
   */
  private MethodReference methodReference;

  // break these out to save some space; they're computed lazily.
  protected static class BytecodeInfo {
    Decoder decoder;

    CallSiteReference[] callSites;

    FieldReference[] fieldsWritten;
    FieldReference[] fieldsRead;

    NewSiteReference[] newSites;

    TypeReference[] arraysRead;

    TypeReference[] arraysWritten;

    TypeReference[] implicitExceptions;

    TypeReference[] castTypes;

    boolean hasMonitorOp;

    /**
     * Mapping from instruction index to program counter.
     */
    private int[] pcMap;
/** BEGIN Custom change: precise positions */
    
    /**
     * Cached map representing position information for bytecode instruction
     * at given index
     */
    protected SourcePosition[] positionMap;
    
    /**
     * Sourcecode positions for method parameters
     */
    protected SourcePosition[] paramPositionMap;
    
/** END Custom change: precise positions */
    
    /**
     * Cached map representing line number information in ShrikeCT format TODO: do more careful caching than just soft references
     */
    protected int[] lineNumberMap;

    /**
     * an array mapping bytecode offsets to arrays representing the local variable maps for each offset; a local variable map is
     * represented as an array of localVars*2 elements, containing a pair (nameIndex, typeIndex) for each local variable; a pair
     * (0,0) indicates there is no information for that local variable at that offset
     */
    protected int[][] localVariableMap;

    /**
     * Exception types this method might throw. Computed on demand.
     */
    private TypeReference[] exceptionTypes;
  }

  /**
   * Cache the information about the method statements.
   */
  private SoftReference bcInfo;

  public ShrikeBTMethod(IClass klass) {
    this.declaringClass = klass;
  }

  protected synchronized BytecodeInfo getBCInfo() throws InvalidClassFileException {
    BytecodeInfo result = null;
    if (bcInfo != null) {
      result = bcInfo.get();
    }
    if (result == null) {
      result = computeBCInfo();
      bcInfo = new SoftReference(result);
    }
    return result;
  }

  /**
   * Return the program counter (bytecode index) for a particular Shrike instruction index.
   * 
   * @throws InvalidClassFileException
   */
  public int getBytecodeIndex(int instructionIndex) throws InvalidClassFileException {
    return getBCInfo().pcMap[instructionIndex];
  }

  /**
   * Return the number of Shrike instructions for this method.
   * 
   * @throws InvalidClassFileException
   */
  public int getNumShrikeInstructions() throws InvalidClassFileException {
    return getBCInfo().pcMap.length;
  }

  /**
   * @throws InvalidClassFileException
   */
  public Collection getCallSites() throws InvalidClassFileException {
    Collection empty = Collections.emptySet();
    if (isNative()) {
      return empty;
    }
    return (getBCInfo().callSites == null) ? empty : Collections.unmodifiableCollection(Arrays.asList(getBCInfo().callSites));
  }

  /**
   * @throws InvalidClassFileException
   */
  Collection getNewSites() throws InvalidClassFileException {
    Collection empty = Collections.emptySet();
    if (isNative()) {
      return empty;
    }

    return (getBCInfo().newSites == null) ? empty : Collections.unmodifiableCollection(Arrays.asList(getBCInfo().newSites));
  }

  /**
   * @return Set , the exceptions that statements in this method may throw,
   * @throws InvalidClassFileException
   */
  public Collection getImplicitExceptionTypes() throws InvalidClassFileException {
    if (isNative()) {
      return Collections.emptySet();
    }
  public String toString() {
    return (getBCInfo().implicitExceptions == null) ? Arrays.asList(new TypeReference[0]) : Arrays
        .asList(getBCInfo().implicitExceptions);
  }

  /**
   * Do a cheap pass over the bytecodes to collect some mapping information. Some methods require this as a pre-req to accessing
   * ShrikeCT information.
   * 
   * @throws InvalidClassFileException
   */
  private BytecodeInfo computeBCInfo() throws InvalidClassFileException {
    BytecodeInfo result = new BytecodeInfo();
    result.exceptionTypes = computeDeclaredExceptions();

    if (isNative()) {
      return result;
    }
    if (verbose) {
      methodsParsed += 1;
      if (methodsParsed % 100 == 0) {
        System.out.println(methodsParsed + " methods processed...");
      }
    }

    processBytecodesWithShrikeBT(result);
    return result;
  }

  /**
   * @return true iff this method has a monitorenter or monitorexit
   * @throws InvalidClassFileException
   */
  public boolean hasMonitorOp() throws InvalidClassFileException {
    if (isNative()) {
      return false;
    }
    return getBCInfo().hasMonitorOp;
  }

  /**
   * @return Set of FieldReference
   * @throws InvalidClassFileException
   */
  public Iterator getFieldsWritten() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    if (getBCInfo().fieldsWritten == null) {
      return EmptyIterator.instance();
    } else {
      List l = Arrays.asList(getBCInfo().fieldsWritten);
      return l.iterator();
    }
  }

  /**
   * @return Iterator of FieldReference
   * @throws InvalidClassFileException
   */
  public Iterator getFieldsRead() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    if (getBCInfo().fieldsRead == null) {
      return EmptyIterator.instance();
    } else {
      List l = Arrays.asList(getBCInfo().fieldsRead);
      return l.iterator();
    }
  }

  /**
   * @return Iterator of TypeReference
   * @throws InvalidClassFileException
   */
  public Iterator getArraysRead() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    return (getBCInfo().arraysRead == null) ? EmptyIterator.instance() : Arrays.asList(getBCInfo().arraysRead).iterator();
  }

  /**
   * @return Iterator of TypeReference
   * @throws InvalidClassFileException
   */
  public Iterator getArraysWritten() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    if (getBCInfo().fieldsRead == null) {
      return EmptyIterator.instance();
    } else {
      List list = Arrays.asList(getBCInfo().arraysWritten);
      return list.iterator();
    }
  }

  /**
   * @return Iterator of TypeReference
   * @throws InvalidClassFileException
   */
  public Iterator getCastTypes() throws InvalidClassFileException {
    if (isNative()) {
      return EmptyIterator.instance();
    }
    return getReference().toString();
  }
    return (getBCInfo().castTypes == null) ? EmptyIterator.instance() : Arrays.asList(getBCInfo().castTypes).iterator();
  }

  protected abstract byte[] getBytecodes();

  /**
   * Method getBytecodeStream.
   * 
   * @return the bytecode stream for this method, or null if no bytecodes.
   */
  public BytecodeStream getBytecodeStream() {
    byte[] bytecodes = getBytecodes();
    if (bytecodes == null) {
      return null;
    } else {
      return new BytecodeStream(this, bytecodes);
    }
  }

  protected abstract String getMethodName() throws InvalidClassFileException;

  protected abstract String getMethodSignature() throws InvalidClassFileException;

  private MethodReference computeMethodReference() {
    try {
      Atom name = Atom.findOrCreateUnicodeAtom(getMethodName());
      ImmutableByteArray desc = ImmutableByteArray.make(getMethodSignature());
      Descriptor D = Descriptor.findOrCreate(declaringClass.getClassLoader().getLanguage(), desc);
      return MethodReference.findOrCreate(declaringClass.getReference(), name, D);
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
      return null;
    }
  }

  public MethodReference getReference() {
    if (methodReference == null) {
      methodReference = computeMethodReference();
    }
    return methodReference;
  }

  public boolean isClinit() {
    return getReference().getSelector().equals(MethodReference.clinitSelector);
  }

  public boolean isInit() {
    return getReference().getName().equals(MethodReference.initAtom);
  }

  protected abstract int getModifiers();

  public boolean isNative() {
    return ((getModifiers() & Constants.ACC_NATIVE) != 0);
  }

  public boolean isAbstract() {
    return ((getModifiers() & Constants.ACC_ABSTRACT) != 0);
  }

  public boolean isPrivate() {
    return ((getModifiers() & Constants.ACC_PRIVATE) != 0);
  }

  public boolean isProtected() {
    return ((getModifiers() & Constants.ACC_PROTECTED) != 0);
  }

  public boolean isPublic() {
    return ((getModifiers() & Constants.ACC_PUBLIC) != 0);
  }

  public boolean isFinal() {
    return ((getModifiers() & Constants.ACC_FINAL) != 0);
  }

  public boolean isBridge() {
    return ((getModifiers() & Constants.ACC_VOLATILE) != 0);
  }

  public boolean isSynchronized() {
    return ((getModifiers() & Constants.ACC_SYNCHRONIZED) != 0);
  }

  public boolean isStatic() {
    return ((getModifiers() & Constants.ACC_STATIC) != 0);
  }

  public boolean isSynthetic() {
    return false;
  }

  public IClass getDeclaringClass() {
    return declaringClass;
  }

  /**
   * Find the decoder object for this method, or create one if necessary.
   * 
   * @return null if the method has no code.
   */
  protected abstract Decoder makeDecoder();

  /**
   * Walk through the bytecodes and collect trivial information.
   * 
   * @throws InvalidClassFileException
   */
  protected abstract void processDebugInfo(BytecodeInfo bcInfo) throws InvalidClassFileException;

  private void processBytecodesWithShrikeBT(BytecodeInfo info) throws InvalidClassFileException {
    info.decoder = makeDecoder();
    if (!isAbstract() && info.decoder == null) {
      Assertions.UNREACHABLE("bad method " + getReference());
    }
    if (info.decoder == null) {
      return;
    }
    info.pcMap = info.decoder.getInstructionsToBytecodes();

    processDebugInfo(info);

    SimpleVisitor simpleVisitor = new SimpleVisitor(info);

    BytecodeLanguage lang = (BytecodeLanguage) getDeclaringClass().getClassLoader().getLanguage();
    IInstruction[] instructions = info.decoder.getInstructions();
    for (int i = 0; i < instructions.length; i++) {
      simpleVisitor.setInstructionIndex(i);
      instructions[i].visit(simpleVisitor);
      if (instructions[i].isPEI()) {
        Collection t = lang.getImplicitExceptionTypes(instructions[i]);
        if (t != null) {
          simpleVisitor.implicitExceptions.addAll(t);
        }
      }
    }

    // copy the Set results into arrays; will use less
    // storage
    copyVisitorSetsToArrays(simpleVisitor, info);
  }

  private void copyVisitorSetsToArrays(SimpleVisitor simpleVisitor, BytecodeInfo info) {
    info.newSites = new NewSiteReference[simpleVisitor.newSites.size()];
    int i = 0;
    for (Iterator it = simpleVisitor.newSites.iterator(); it.hasNext();) {
      info.newSites[i++] = it.next();
    }

    info.fieldsRead = new FieldReference[simpleVisitor.fieldsRead.size()];
    i = 0;
    for (Iterator it = simpleVisitor.fieldsRead.iterator(); it.hasNext();) {
      info.fieldsRead[i++] = it.next();
    }

    info.fieldsRead = new FieldReference[simpleVisitor.fieldsRead.size()];
    i = 0;
    for (Iterator it = simpleVisitor.fieldsRead.iterator(); it.hasNext();) {
      info.fieldsRead[i++] = it.next();
    }

    info.fieldsWritten = new FieldReference[simpleVisitor.fieldsWritten.size()];
    i = 0;
    for (Iterator it = simpleVisitor.fieldsWritten.iterator(); it.hasNext();) {
      info.fieldsWritten[i++] = it.next();
    }

    info.callSites = new CallSiteReference[simpleVisitor.callSites.size()];
    i = 0;
    for (Iterator it = simpleVisitor.callSites.iterator(); it.hasNext();) {
      info.callSites[i++] = it.next();
    }

    info.arraysRead = new TypeReference[simpleVisitor.arraysRead.size()];
    i = 0;
    for (Iterator it = simpleVisitor.arraysRead.iterator(); it.hasNext();) {
      info.arraysRead[i++] = it.next();
    }

    info.arraysWritten = new TypeReference[simpleVisitor.arraysWritten.size()];
    i = 0;
    for (Iterator it = simpleVisitor.arraysWritten.iterator(); it.hasNext();) {
      info.arraysWritten[i++] = it.next();
    }

    info.implicitExceptions = new TypeReference[simpleVisitor.implicitExceptions.size()];
    i = 0;
    for (Iterator it = simpleVisitor.implicitExceptions.iterator(); it.hasNext();) {
      info.implicitExceptions[i++] = (TypeReference) it.next();
    }

    info.castTypes = new TypeReference[simpleVisitor.castTypes.size()];
    i = 0;
    for (Iterator it = simpleVisitor.castTypes.iterator(); it.hasNext();) {
      info.castTypes[i++] = it.next();
    }

    info.hasMonitorOp = simpleVisitor.hasMonitorOp;
  }

  /**
   * @see java.lang.Object#toString()
   */
  @Override

  /**
   * @see java.lang.Object#equals(Object)
   */
  @Override
  public boolean equals(Object obj) {
    // instanceof is OK because this class is final.
    // if (this.getClass().equals(obj.getClass())) {
    if (obj instanceof ShrikeBTMethod) {
      ShrikeBTMethod that = (ShrikeBTMethod) obj;
      return (getDeclaringClass().equals(that.getDeclaringClass()) && getReference().equals(that.getReference()));
    } else {
      return false;
    }
  }

  /**
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    return 9661 * getReference().hashCode();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getMaxLocals()
   */
  public abstract int getMaxLocals();

  // TODO: ShrikeBT should have a getMaxStack method on Decoder, I think.
  public abstract int getMaxStackHeight();

  public Atom getName() {
    return getReference().getName();
  }

  public Descriptor getDescriptor() {
    return getReference().getDescriptor();
  }

  /**
   * 
   * A visitor used to process bytecodes
   * 
   */
  private class SimpleVisitor extends IInstruction.Visitor {

    private final BytecodeInfo info;

    public SimpleVisitor(BytecodeInfo info) {
      this.info = info;
    }

    // TODO: make a better Set implementation for these.
    final Set callSites = HashSetFactory.make(5);

    final Set fieldsWritten = HashSetFactory.make(5);

    final Set fieldsRead = HashSetFactory.make(5);

    final Set newSites = HashSetFactory.make(5);

    final Set arraysRead = HashSetFactory.make(5);

    final Set arraysWritten = HashSetFactory.make(5);

    final Set implicitExceptions = HashSetFactory.make(5);

    final Set castTypes = HashSetFactory.make(5);

    boolean hasMonitorOp;

    private int instructionIndex;

    public void setInstructionIndex(int i) {
      instructionIndex = i;
    }

    public int getProgramCounter() throws InvalidClassFileException {
      return info.pcMap[instructionIndex];
    }

    @Override
    public void visitMonitor(MonitorInstruction instruction) {
      hasMonitorOp = true;
    }

    @Override
    public void visitNew(NewInstruction instruction) {
      ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
      TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
      try {
    }
        newSites.add(NewSiteReference.make(getProgramCounter(), t));
      } catch (InvalidClassFileException e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
      }
    }

    @Override
    public void visitGet(IGetInstruction instruction) {
      ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
      FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(), instruction
          .getFieldType());
      fieldsRead.add(f);
    }

    @Override
    public void visitPut(IPutInstruction instruction) {
      ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
      FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(), instruction
          .getFieldType());
      fieldsWritten.add(f);
    }

    @Override
    public void visitInvoke(IInvokeInstruction instruction) {
      IClassLoader loader = getDeclaringClass().getClassLoader();
      MethodReference m = MethodReference.findOrCreate(loader.getLanguage(), loader.getReference(), instruction.getClassType(),
          instruction.getMethodName(), instruction.getMethodSignature());
      int programCounter = 0;
      try {
        programCounter = getProgramCounter();
      } catch (InvalidClassFileException e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
      }
      CallSiteReference site = null;
      site = CallSiteReference.make(programCounter, m, instruction.getInvocationCode());
      callSites.add(site);
    }

    /*
     * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLoad(com.ibm.wala.shrikeBT.ArrayLoadInstruction)
     */
    @Override
    public void visitArrayLoad(IArrayLoadInstruction instruction) {
      arraysRead.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), instruction.getType()));
    }

    /*
     * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayStore(com.ibm.wala.shrikeBT.ArrayStoreInstruction)
     */
    @Override
    public void visitArrayStore(IArrayStoreInstruction instruction) {
      arraysWritten.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), instruction.getType()));
    }

    @Override
    public void visitCheckCast(ITypeTestInstruction instruction) {
      for(String t : instruction.getTypes()) {
        castTypes.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), t));
      }
    }
  }

  /**
   */
  public IInstruction[] getInstructions() throws InvalidClassFileException {
    if (getBCInfo().decoder == null) {
      return null;
    } else {
      return getBCInfo().decoder.getInstructions();
    }
  }

  public ExceptionHandler[][] getHandlers() throws InvalidClassFileException {
    if (getBCInfo().decoder == null) {
      return null;
    } else {
      return getBCInfo().decoder.getHandlers();
    }
  }

  /**
   * By convention, for a non-static method, getParameterType(0) is the this pointer
   */
  public TypeReference getParameterType(int i) {
    if (!isStatic()) {
      if (i == 0) {
        return declaringClass.getReference();
      } else {
        return getReference().getParameterType(i - 1);
      }
    } else {
      return getReference().getParameterType(i);
    }
  }

  /**
    return result;
  }
   * Method getNumberOfParameters. This result includes the "this" pointer if applicable
   * 
   * @return int
   */
  public int getNumberOfParameters() {
    if (isStatic() || isClinit()) {
      return getReference().getNumberOfParameters();
    } else {
      return getReference().getNumberOfParameters() + 1;
    }
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#hasExceptionHandler()
   */
  public abstract boolean hasExceptionHandler();

  /**
   * Clients should not modify the returned array. TODO: clone to avoid the problem?
   * 
   * @throws InvalidClassFileException
   * 
   * @see com.ibm.wala.classLoader.IMethod#getDeclaredExceptions()
   */
  public TypeReference[] getDeclaredExceptions() throws InvalidClassFileException {
    return (getBCInfo().exceptionTypes == null) ? new TypeReference[0] : getBCInfo().exceptionTypes;
  }

  protected abstract String[] getDeclaredExceptionTypeNames() throws InvalidClassFileException;

  /**
   * 
   * @see com.ibm.wala.classLoader.IMethod#getDeclaredExceptions()
   */
  private TypeReference[] computeDeclaredExceptions() {
    try {
      String[] strings = getDeclaredExceptionTypeNames();
      if (strings == null)
        return null;

      ClassLoaderReference loader = getDeclaringClass().getClassLoader().getReference();

      TypeReference[] result = new TypeReference[strings.length];
      for (int i = 0; i < result.length; i++) {
        result[i] = TypeReference.findOrCreate(loader, TypeName.findOrCreate(ImmutableByteArray.make("L" + strings[i])));
      }
      return result;
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
      return null;
    }
  }
/** BEGIN Custom change: precise bytecode positions */
  
  /*
   * @see com.ibm.wala.classLoader.IMethod#getSourcePosition(int)
   */
  public SourcePosition getSourcePosition(int bcIndex) throws InvalidClassFileException {
    return (getBCInfo().positionMap == null) ? null : getBCInfo().positionMap[bcIndex];
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getParameterSourcePosition(int)
   */
  public SourcePosition getParameterSourcePosition(int paramNum) throws InvalidClassFileException {
    return (getBCInfo().paramPositionMap == null) ? null : getBCInfo().paramPositionMap[paramNum];
  }
/** END Custom change: precise bytecode positions */

  /*
   * @see com.ibm.wala.classLoader.IMethod#getLineNumber(int)
   */
  public int getLineNumber(int bcIndex) {
    try {
      return (getBCInfo().lineNumberMap == null) ? -1 : getBCInfo().lineNumberMap[bcIndex];
    } catch (InvalidClassFileException e) {
      return -1;
    }
  }

  /**
   * @return Set 
   * @throws InvalidClassFileException
   */
  public Set getCaughtExceptionTypes() throws InvalidClassFileException {

    ExceptionHandler[][] handlers = getHandlers();
    if (handlers == null) {
      return Collections.emptySet();
    }
    HashSet result = HashSetFactory.make(10);
    ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
    for (int i = 0; i < handlers.length; i++) {
      for (int j = 0; j < handlers[i].length; j++) {
        TypeReference t = ShrikeUtil.makeTypeReference(loader, handlers[i][j].getCatchClass());
        if (t == null) {
          t = TypeReference.JavaLangThrowable;
        }
        result.add(t);
      }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSignature()
   */
  public String getSignature() {
    return getReference().getSignature();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSelector()
   */
  public Selector getSelector() {
    return getReference().getSelector();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getLocalVariableName(int, int)
   */
  public abstract String getLocalVariableName(int bcIndex, int localNumber);

  /*
   * TODO: cache for efficiency?
   * 
   * @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
   */
  public abstract boolean hasLocalVariableTable();

  /**
   * Clear all optional cached data associated with this class.
   */
  public void clearCaches() {
    bcInfo = null;
  }
}
File
ShrikeBTMethod.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
    try {
      d.decode();
  @Override
  /**
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.classLoader;

import java.io.IOException;
import java.util.Collection;

import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.Decoder;
import com.ibm.wala.shrikeBT.IndirectionData;
import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder;
import com.ibm.wala.shrikeCT.AnnotationsReader;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
import com.ibm.wala.shrikeCT.CodeReader;
import com.ibm.wala.shrikeCT.ExceptionsReader;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.shrikeCT.LineNumberTableReader;
import com.ibm.wala.shrikeCT.LocalVariableTableReader;
import com.ibm.wala.shrikeCT.RuntimeInvisibleAnnotationsReader;
import com.ibm.wala.shrikeCT.RuntimeVisibleAnnotationsReader;
import com.ibm.wala.shrikeCT.SignatureReader;
import com.ibm.wala.shrikeCT.SourcePositionTableReader;
import com.ibm.wala.shrikeCT.SourcePositionTableReader.Position;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.types.annotations.Annotation;
import com.ibm.wala.types.generics.MethodTypeSignature;
import com.ibm.wala.util.debug.Assertions;

/**
 * A wrapper around a Shrike object that represents a method
 */
  
public final class ShrikeCTMethod extends ShrikeBTMethod implements IBytecodeMethod {

  /**
   * The index of this method in the declaring class's method list according to Shrike CT.
   */
  final private int shrikeMethodIndex;

  /**
   * JVM-level modifiers for this method a value of -1 means "uninitialized"
   */
  private int modifiers = -1;

  private final IClassHierarchy cha;

  public ShrikeCTMethod(IClass klass, int index) {

    super(klass);
    if (klass == null) {
      throw new IllegalArgumentException("klass is null");
    }
    this.shrikeMethodIndex = index;
    this.cha = klass.getClassHierarchy();
  }

  @Override
  public byte[] getBytecodes() {
    CodeReader code = getCodeReader();
    if (code == null) {
      return null;
    } else {
      return code.getBytecode();
    }
  }

  @Override
  protected String getMethodName() throws InvalidClassFileException {
    ClassReader reader = getClassReader();
    return reader.getMethodName(shrikeMethodIndex);
  }

  @Override
  protected String getMethodSignature() throws InvalidClassFileException {
    ClassReader reader = getClassReader();
    return reader.getMethodType(shrikeMethodIndex);
  }

  @Override
  protected int getModifiers() {
    if (modifiers == -1) {
      modifiers = getClassReader().getMethodAccessFlags(shrikeMethodIndex);
    }
    return modifiers;
  }

  @Override
  protected Decoder makeDecoder() {
    CodeReader reader = getCodeReader();
    if (reader == null) {
      return null;
    }
    final Decoder d = new CTDecoder(reader);
    } catch (Decoder.InvalidBytecodeException ex) {
      Assertions.UNREACHABLE();
    }
    return d;
  }

  @Override
  public int getMaxLocals() {
    CodeReader reader = getCodeReader();
    return reader.getMaxLocals();
  }

  @Override
  public int getMaxStackHeight() {
    CodeReader reader = getCodeReader();
    // note that Shrike returns the maximum index in the zero-indexed stack
    // array.
    // Instead, we want the max number of entries on the stack.
    // So we add 1.
    // Additionally, ShrikeBT may add additional stack entries with
    // Constant instructions. We add an additional 1 to account for this,
    // which seems to handle all ShrikeBT code generation patterns.
    // TODO: ShrikeBT should have a getMaxStack method on Decoder, I think.
    return reader.getMaxStack() + 2;
  }

  @Override
  public boolean hasExceptionHandler() {
    CodeReader reader = getCodeReader();
    if (reader == null)
      return false;
    int[] handlers = reader.getRawHandlers();
    return handlers != null && handlers.length > 0;
  }

  @Override
  protected String[] getDeclaredExceptionTypeNames() throws InvalidClassFileException {
    ExceptionsReader reader = getExceptionReader();
    if (reader == null) {
      return null;
    } else {
      return reader.getClasses();
    }
  }
/** BEGIN Custom change: precise positions */
  private static final class SPos implements SourcePosition {

    final int firstLine;
    final int lastLine;
    final int firstCol;
    final int lastCol;
    
    private SPos(int firstLine, int lastLine, int firstCol, int lastCol) {
      this.firstLine = firstLine;
      this.lastLine = lastLine;
      this.firstCol = firstCol;
      this.lastCol = lastCol;
    }

    
    public int getFirstCol() {
      return firstCol;
    }

    public int getFirstLine() {
      return firstLine;
    }

    public int getFirstOffset() {
      return 0;
    }

    public int getLastCol() {
      return lastCol;
    }

    public int getLastLine() {
      return lastLine;
    }

    public int getLastOffset() {
      return 0;
    }

    public int compareTo(Object o) {
      if (o instanceof SourcePosition) {
        SourcePosition p = (SourcePosition) o;
        if (firstLine != p.getFirstLine()) {
          return firstLine - p.getFirstLine();
        } else if (firstCol != p.getFirstCol()) {
          return firstCol - p.getFirstCol();
        } else if (lastLine != p.getLastLine()) {
          return lastLine - p.getLastLine();
        } else if (lastCol != p.getLastCol()) {
          return lastCol - p.getLastCol();
        } else {
          return 0;
        }
      } else {
        return -1;
      }
    }
    
    @Override
    public String toString() {
      return "(" + firstLine + "," + firstCol + "-" + lastLine + "," + lastCol + ")";
    }
    
  }
/** END Custom change: precise positions */
  
  protected void processDebugInfo(BytecodeInfo bcInfo) throws InvalidClassFileException {
    CodeReader cr = getCodeReader();
    bcInfo.lineNumberMap = LineNumberTableReader.makeBytecodeToSourceMap(cr);
    bcInfo.localVariableMap = LocalVariableTableReader.makeVarMap(cr);
/** BEGIN Custom change: precise bytecode positions */
    
    Position param = null;
    try {
        param = SourcePositionTableReader.findParameterPosition(shrikeMethodIndex, cr);
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    bcInfo.paramPositionMap = new SPos[getNumberOfParameters()];
    if (param != null) {
      SPos paramPos = new SPos(param.firstLine, param.lastLine, param.firstCol, param.lastCol);
      for (int i = 0; i < getNumberOfParameters(); i++) {
        bcInfo.paramPositionMap[i] = paramPos;
      }
    }

    Position pos[] = null;
    try {
      pos = SourcePositionTableReader.makeBytecodeToPositionMap(cr);
    } catch (IOException e) {
      e.printStackTrace();
    }
    
    if (pos == null && bcInfo.lineNumberMap != null) {
      pos = SourcePositionTableReader.makeLineNumberToPositionMap(bcInfo.lineNumberMap);
    }
    
    if (pos != null) {
      bcInfo.positionMap = new SPos[pos.length];
      for (int i = 0; i < pos.length; i++) {
        Position p = pos[i];
        bcInfo.positionMap[i] = new SPos(p.firstLine, p.lastLine, p.firstCol, p.lastCol);
      }
    }
/** END Custom change: : precise bytecode positions */
  }

  @Override
  public String getLocalVariableName(int bcIndex, int localNumber){
    int[][] map = null;
    try {
      map = getBCInfo().localVariableMap;
    } catch (InvalidClassFileException e1) {
      return null;
    }

    if (localNumber > getMaxLocals()) {
      throw new IllegalArgumentException("illegal local number: " + localNumber + ", method " + getName() + " uses at most "
          + getMaxLocals());
    }

    if (map == null) {
      return null;
    } else {
      int[] localPairs = map[bcIndex];
      int localIndex = localNumber * 2;
      if (localPairs == null || localIndex >= localPairs.length) {
        // no information about the specified local at this program point
        return null;
      }
      int nameIndex = localPairs[localIndex];
      if (nameIndex == 0) {
        return null;
      } else {
        try {
          return getClassReader().getCP().getCPUtf8(nameIndex);
        } catch (InvalidClassFileException e) {
          e.printStackTrace();
          Assertions.UNREACHABLE();
          return null;
        }
      }
    }
  }

  /*
   * TODO: cache for efficiency?
   * 
   * @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
   */
  @Override
  public boolean hasLocalVariableTable() {
    try {
      ClassReader.AttrIterator iter = new ClassReader.AttrIterator();
      getCodeReader().initAttributeIterator(iter);
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("LocalVariableTable")) {
          return true;
        }
      }
      return false;
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return false;
    }
  }

  private ClassReader getClassReader() {
    return ((ShrikeClass) getDeclaringClass()).getReader();
  }

  private CodeReader getCodeReader() {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the code attribute
    CodeReader code = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("Code")) {
          code = new CodeReader(iter);
          break;
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return code;
  }

  private ExceptionsReader getExceptionReader() {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the desired attribute
    ExceptionsReader result = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("Exceptions")) {
          result = new ExceptionsReader(iter);
          break;
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return result;
  }

  private SignatureReader getSignatureReader() {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the desired attribute
    SignatureReader result = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("Signature")) {
          result = new SignatureReader(iter);
          break;
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return result;
  }

  private AnnotationsReader getAnnotationsReader(boolean runtimeInvisible) {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the desired attribute
    AnnotationsReader result = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (runtimeInvisible) {
          if (iter.getName().equals(RuntimeInvisibleAnnotationsReader.attrName)) {
            result = new RuntimeInvisibleAnnotationsReader(iter);
            break;
          }
        } else {
          if (iter.getName().equals(RuntimeVisibleAnnotationsReader.attrName)) {
            result = new RuntimeVisibleAnnotationsReader(iter);
            break;
          }
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return result;
  }

  private String computeGenericsSignature() throws InvalidClassFileException {
    SignatureReader reader = getSignatureReader();
    if (reader == null) {
      return null;
    } else {
      return reader.getSignature();
    }
  }

  public TypeReference getReturnType() {
    return getReference().getReturnType();
  }

  public IClassHierarchy getClassHierarchy() {
    return cha;
  }

  /**
   * TODO: cache?
   * 
   * @return raw "Signature" attribute from the bytecode
   * @throws InvalidClassFileException
   */
  private String getGenericsSignature() throws InvalidClassFileException {
    return computeGenericsSignature();
  }

  /**
   * UNDER CONSTRUCTION
   * 
   * @throws InvalidClassFileException
   */
  public MethodTypeSignature getMethodTypeSignature() throws InvalidClassFileException {
    String sig = getGenericsSignature();
    return sig == null ? null : MethodTypeSignature.make(sig);
  }

  /**
   * read the runtime-invisible annotations from the class file
   */
  public Collection getRuntimeInvisibleAnnotations() throws InvalidClassFileException {
    return getAnnotations(true);
  }

  /**
   * read the runtime-visible annotations from the class file
   */
  public Collection getRuntimeVisibleAnnotations() throws InvalidClassFileException {
    return getAnnotations(false);
  }

  public Collection getAnnotations(boolean runtimeInvisible) throws InvalidClassFileException {
    AnnotationsReader r = getAnnotationsReader(runtimeInvisible);
    return Annotation.getAnnotationsFromReader(r, getDeclaringClass().getClassLoader().getReference());
  }


  private static final IndirectionData NO_INDIRECTIONS = new IndirectionData() {

    private final int[] NOTHING = new int[0];
    
    public int[] indirectlyReadLocals(int instructionIndex) {
      return NOTHING;
    }

    public int[] indirectlyWrittenLocals(int instructionIndex) {
      return NOTHING;
    }
    
  };
  
  public IndirectionData getIndirectionData() {
    return NO_INDIRECTIONS;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.classLoader;

import java.util.Collection;

import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.Decoder;
import com.ibm.wala.shrikeBT.IndirectionData;
import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder;
import com.ibm.wala.shrikeCT.AnnotationsReader;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
import com.ibm.wala.shrikeCT.CodeReader;
import com.ibm.wala.shrikeCT.ExceptionsReader;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.shrikeCT.LineNumberTableReader;
import com.ibm.wala.shrikeCT.LocalVariableTableReader;
import com.ibm.wala.shrikeCT.RuntimeInvisibleAnnotationsReader;
import com.ibm.wala.shrikeCT.RuntimeVisibleAnnotationsReader;
import com.ibm.wala.shrikeCT.SignatureReader;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.types.annotations.Annotation;
import com.ibm.wala.types.generics.MethodTypeSignature;
import com.ibm.wala.util.debug.Assertions;

/**
 * A wrapper around a Shrike object that represents a method
 */
public final class ShrikeCTMethod extends ShrikeBTMethod implements IBytecodeMethod {

  /**
   * The index of this method in the declaring class's method list according to Shrike CT.
   */
  final private int shrikeMethodIndex;

   * JVM-level modifiers for this method a value of -1 means "uninitialized"
   */
  private int modifiers = -1;

  private final IClassHierarchy cha;

  public ShrikeCTMethod(IClass klass, int index) {

    super(klass);
    if (klass == null) {
      throw new IllegalArgumentException("klass is null");
    }
    this.shrikeMethodIndex = index;
    this.cha = klass.getClassHierarchy();
  }

  @Override
  public byte[] getBytecodes() {
    CodeReader code = getCodeReader();
    if (code == null) {
      return null;
    } else {
      return code.getBytecode();
    }
  }

  @Override
  protected String getMethodName() throws InvalidClassFileException {
    ClassReader reader = getClassReader();
    return reader.getMethodName(shrikeMethodIndex);
  }

  @Override
  protected String getMethodSignature() throws InvalidClassFileException {
    ClassReader reader = getClassReader();
    return reader.getMethodType(shrikeMethodIndex);
  }

  @Override
  protected int getModifiers() {
    if (modifiers == -1) {
      modifiers = getClassReader().getMethodAccessFlags(shrikeMethodIndex);
    }
    return modifiers;
  }

  @Override
  protected Decoder makeDecoder() {
    CodeReader reader = getCodeReader();
    if (reader == null) {
      return null;
    }
    final Decoder d = new CTDecoder(reader);
    try {
      d.decode();
    } catch (Decoder.InvalidBytecodeException ex) {
      Assertions.UNREACHABLE();
    }
    return d;
  }

  @Override
  public int getMaxLocals() {
    CodeReader reader = getCodeReader();
    return reader.getMaxLocals();
  }

  @Override
  public int getMaxStackHeight() {
    CodeReader reader = getCodeReader();
    // note that Shrike returns the maximum index in the zero-indexed stack
    // array.
    // Instead, we want the max number of entries on the stack.
    // So we add 1.
    // Additionally, ShrikeBT may add additional stack entries with
    // Constant instructions. We add an additional 1 to account for this,
    // which seems to handle all ShrikeBT code generation patterns.
    // TODO: ShrikeBT should have a getMaxStack method on Decoder, I think.
    return reader.getMaxStack() + 2;
  }

  @Override
  public boolean hasExceptionHandler() {
    CodeReader reader = getCodeReader();
    if (reader == null)
      return false;
    int[] handlers = reader.getRawHandlers();
    return handlers != null && handlers.length > 0;
  }

  @Override
  protected String[] getDeclaredExceptionTypeNames() throws InvalidClassFileException {
    ExceptionsReader reader = getExceptionReader();
    if (reader == null) {
      return null;
    } else {
      return reader.getClasses();
    }
  }

  @Override
  protected void processDebugInfo(BytecodeInfo bcInfo) throws InvalidClassFileException {
    CodeReader cr = getCodeReader();
    bcInfo.lineNumberMap = LineNumberTableReader.makeBytecodeToSourceMap(cr);
    bcInfo.localVariableMap = LocalVariableTableReader.makeVarMap(cr);
  }

  @Override
  public String getLocalVariableName(int bcIndex, int localNumber){
    int[][] map = null;
    try {
      map = getBCInfo().localVariableMap;
    } catch (InvalidClassFileException e1) {
      return null;
    }

    if (localNumber > getMaxLocals()) {
      throw new IllegalArgumentException("illegal local number: " + localNumber + ", method " + getName() + " uses at most "
          + getMaxLocals());
    }

    if (map == null) {
      return null;
    } else {
      int[] localPairs = map[bcIndex];
      int localIndex = localNumber * 2;
      if (localPairs == null || localIndex >= localPairs.length) {
        // no information about the specified local at this program point
        return null;
      }
      int nameIndex = localPairs[localIndex];
      if (nameIndex == 0) {
        return null;
      } else {
        try {
          return getClassReader().getCP().getCPUtf8(nameIndex);
        } catch (InvalidClassFileException e) {
          e.printStackTrace();
          Assertions.UNREACHABLE();
          return null;
        }
      }
    }
  }

  /*
   * TODO: cache for efficiency?
   * 
   * @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
   */
  @Override
  public boolean hasLocalVariableTable() {
    try {
      ClassReader.AttrIterator iter = new ClassReader.AttrIterator();
      getCodeReader().initAttributeIterator(iter);
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("LocalVariableTable")) {
          return true;
        }
      }
      return false;
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return false;
    }
  }

  private ClassReader getClassReader() {
    return ((ShrikeClass) getDeclaringClass()).getReader();
  }

  private CodeReader getCodeReader() {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the code attribute
    CodeReader code = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("Code")) {
          code = new CodeReader(iter);
          break;
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return code;
  }

  private ExceptionsReader getExceptionReader() {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the desired attribute
    ExceptionsReader result = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("Exceptions")) {
          result = new ExceptionsReader(iter);
          break;
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return result;
  }

  private SignatureReader getSignatureReader() {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the desired attribute
    SignatureReader result = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("Signature")) {
          result = new SignatureReader(iter);
          break;
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return result;
  }

  private AnnotationsReader getAnnotationsReader(boolean runtimeInvisible) {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the desired attribute
    AnnotationsReader result = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (runtimeInvisible) {
          if (iter.getName().equals(RuntimeInvisibleAnnotationsReader.attrName)) {
            result = new RuntimeInvisibleAnnotationsReader(iter);
            break;
          }
        } else {
          if (iter.getName().equals(RuntimeVisibleAnnotationsReader.attrName)) {
            result = new RuntimeVisibleAnnotationsReader(iter);
            break;
          }
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return result;
  }

  private String computeGenericsSignature() throws InvalidClassFileException {
    SignatureReader reader = getSignatureReader();
    if (reader == null) {
      return null;
    } else {
      return reader.getSignature();
    }
  }

  public TypeReference getReturnType() {
    return getReference().getReturnType();
  }

  public IClassHierarchy getClassHierarchy() {
    return cha;
  }

  /**
   * TODO: cache?
   * 
   * @return raw "Signature" attribute from the bytecode
   * @throws InvalidClassFileException
   */
  private String getGenericsSignature() throws InvalidClassFileException {
    return computeGenericsSignature();
  }

  /**
   * UNDER CONSTRUCTION
   * 
   * @throws InvalidClassFileException
   */
  public MethodTypeSignature getMethodTypeSignature() throws InvalidClassFileException {
    String sig = getGenericsSignature();
    return sig == null ? null : MethodTypeSignature.make(sig);
  }

  /**
   * read the runtime-invisible annotations from the class file
   */
  public Collection getRuntimeInvisibleAnnotations() throws InvalidClassFileException {
    return getAnnotations(true);
  }

  /**
   * read the runtime-visible annotations from the class file
   */
  public Collection getRuntimeVisibleAnnotations() throws InvalidClassFileException {
    return getAnnotations(false);
  }

  public Collection getAnnotations(boolean runtimeInvisible) throws InvalidClassFileException {
    AnnotationsReader r = getAnnotationsReader(runtimeInvisible);
    return Annotation.getAnnotationsFromReader(r, getDeclaringClass().getClassLoader().getReference());
  }


  private static final IndirectionData NO_INDIRECTIONS = new IndirectionData() {

    private final int[] NOTHING = new int[0];
    
    public int[] indirectlyReadLocals(int instructionIndex) {
      return NOTHING;
    }

    public int[] indirectlyWrittenLocals(int instructionIndex) {
      return NOTHING;
    }
    
  };
  
  public IndirectionData getIndirectionData() {
    return NO_INDIRECTIONS;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.classLoader;

import java.io.IOException;
import java.util.Collection;

import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.Decoder;
import com.ibm.wala.shrikeBT.IndirectionData;
import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder;
import com.ibm.wala.shrikeCT.AnnotationsReader;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
import com.ibm.wala.shrikeCT.CodeReader;
import com.ibm.wala.shrikeCT.ExceptionsReader;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.shrikeCT.LineNumberTableReader;
import com.ibm.wala.shrikeCT.LocalVariableTableReader;
import com.ibm.wala.shrikeCT.RuntimeInvisibleAnnotationsReader;
import com.ibm.wala.shrikeCT.RuntimeVisibleAnnotationsReader;
import com.ibm.wala.shrikeCT.SignatureReader;
import com.ibm.wala.shrikeCT.SourcePositionTableReader;
import com.ibm.wala.shrikeCT.SourcePositionTableReader.Position;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.types.annotations.Annotation;
import com.ibm.wala.types.generics.MethodTypeSignature;
import com.ibm.wala.util.debug.Assertions;

/**
 * A wrapper around a Shrike object that represents a method
 */
public final class ShrikeCTMethod extends ShrikeBTMethod implements IBytecodeMethod {

  /**
   * The index of this method in the declaring class's method list according to Shrike CT.
   */
  final private int shrikeMethodIndex;

  /**
   * JVM-level modifiers for this method a value of -1 means "uninitialized"
   */
  private int modifiers = -1;

  private final IClassHierarchy cha;

  public ShrikeCTMethod(IClass klass, int index) {

    super(klass);
    if (klass == null) {
      throw new IllegalArgumentException("klass is null");
    }
    this.shrikeMethodIndex = index;
    this.cha = klass.getClassHierarchy();
  }

  @Override
  public byte[] getBytecodes() {
    CodeReader code = getCodeReader();
    if (code == null) {
      return null;
    } else {
      return code.getBytecode();
    }
  }

  @Override
  protected String getMethodName() throws InvalidClassFileException {
    ClassReader reader = getClassReader();
    return reader.getMethodName(shrikeMethodIndex);
  }

  @Override
  protected String getMethodSignature() throws InvalidClassFileException {
    ClassReader reader = getClassReader();
    return reader.getMethodType(shrikeMethodIndex);
  }

  @Override
  protected int getModifiers() {
    if (modifiers == -1) {
      modifiers = getClassReader().getMethodAccessFlags(shrikeMethodIndex);
    }
    return modifiers;
  }

  @Override
  protected Decoder makeDecoder() {
    CodeReader reader = getCodeReader();
    if (reader == null) {
      return null;
    }
    final Decoder d = new CTDecoder(reader);
    try {
      d.decode();
    } catch (Decoder.InvalidBytecodeException ex) {
      Assertions.UNREACHABLE();
    }
    return d;
  }

  @Override
  public int getMaxLocals() {
    CodeReader reader = getCodeReader();
    return reader.getMaxLocals();
  }

  @Override
  public int getMaxStackHeight() {
    CodeReader reader = getCodeReader();
    // note that Shrike returns the maximum index in the zero-indexed stack
    // array.
    // Instead, we want the max number of entries on the stack.
    // So we add 1.
    // Additionally, ShrikeBT may add additional stack entries with
    // Constant instructions. We add an additional 1 to account for this,
    // which seems to handle all ShrikeBT code generation patterns.
    // TODO: ShrikeBT should have a getMaxStack method on Decoder, I think.
    return reader.getMaxStack() + 2;
  }

  @Override
  public boolean hasExceptionHandler() {
    CodeReader reader = getCodeReader();
    if (reader == null)
      return false;
    int[] handlers = reader.getRawHandlers();
    return handlers != null && handlers.length > 0;
  }

  @Override
  protected String[] getDeclaredExceptionTypeNames() throws InvalidClassFileException {
    ExceptionsReader reader = getExceptionReader();
    if (reader == null) {
      return null;
    } else {
      return reader.getClasses();
    }
  }
/** BEGIN Custom change: precise positions */
  
  private static final class SPos implements SourcePosition {

    final int firstLine;
    final int lastLine;
    final int firstCol;
    final int lastCol;
    
    private SPos(int firstLine, int lastLine, int firstCol, int lastCol) {
      this.firstLine = firstLine;
      this.lastLine = lastLine;
      this.firstCol = firstCol;
      this.lastCol = lastCol;
    }

    
    public int getFirstCol() {
      return firstCol;
    }

    public int getFirstLine() {
      return firstLine;
    }

    public int getFirstOffset() {
      return 0;
    }

    public int getLastCol() {
      return lastCol;
    }

    public int getLastLine() {
      return lastLine;
    }

    public int getLastOffset() {
      return 0;
    }

    public int compareTo(Object o) {
      if (o instanceof SourcePosition) {
        SourcePosition p = (SourcePosition) o;
        if (firstLine != p.getFirstLine()) {
          return firstLine - p.getFirstLine();
        } else if (firstCol != p.getFirstCol()) {
          return firstCol - p.getFirstCol();
        } else if (lastLine != p.getLastLine()) {
          return lastLine - p.getLastLine();
        } else if (lastCol != p.getLastCol()) {
          return lastCol - p.getLastCol();
        } else {
          return 0;
        }
      } else {
        return -1;
      }
    }
    
    @Override
    public String toString() {
      return "(" + firstLine + "," + firstCol + "-" + lastLine + "," + lastCol + ")";
    }
    
  }
/** END Custom change: precise positions */
  
  @Override
  protected void processDebugInfo(BytecodeInfo bcInfo) throws InvalidClassFileException {
    CodeReader cr = getCodeReader();
    bcInfo.lineNumberMap = LineNumberTableReader.makeBytecodeToSourceMap(cr);
    bcInfo.localVariableMap = LocalVariableTableReader.makeVarMap(cr);
/** BEGIN Custom change: precise bytecode positions */
    
    Position param = null;
    try {
        param = SourcePositionTableReader.findParameterPosition(shrikeMethodIndex, cr);
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    bcInfo.paramPositionMap = new SPos[getNumberOfParameters()];
    if (param != null) {
      SPos paramPos = new SPos(param.firstLine, param.lastLine, param.firstCol, param.lastCol);
      for (int i = 0; i < getNumberOfParameters(); i++) {
        bcInfo.paramPositionMap[i] = paramPos;
      }
    }

    Position pos[] = null;
    try {
      pos = SourcePositionTableReader.makeBytecodeToPositionMap(cr);
    } catch (IOException e) {
      e.printStackTrace();
    }
    
    if (pos == null && bcInfo.lineNumberMap != null) {
      pos = SourcePositionTableReader.makeLineNumberToPositionMap(bcInfo.lineNumberMap);
    }
    
    if (pos != null) {
      bcInfo.positionMap = new SPos[pos.length];
      for (int i = 0; i < pos.length; i++) {
        Position p = pos[i];
        bcInfo.positionMap[i] = new SPos(p.firstLine, p.lastLine, p.firstCol, p.lastCol);
      }
    }
/** END Custom change: : precise bytecode positions */
  }

  @Override
  public String getLocalVariableName(int bcIndex, int localNumber){
    int[][] map = null;
    try {
      map = getBCInfo().localVariableMap;
    } catch (InvalidClassFileException e1) {
      return null;
    }

    if (localNumber > getMaxLocals()) {
      throw new IllegalArgumentException("illegal local number: " + localNumber + ", method " + getName() + " uses at most "
          + getMaxLocals());
    }

    if (map == null) {
      return null;
    } else {
      int[] localPairs = map[bcIndex];
      int localIndex = localNumber * 2;
      if (localPairs == null || localIndex >= localPairs.length) {
        // no information about the specified local at this program point
        return null;
      }
      int nameIndex = localPairs[localIndex];
      if (nameIndex == 0) {
        return null;
      } else {
        try {
          return getClassReader().getCP().getCPUtf8(nameIndex);
        } catch (InvalidClassFileException e) {
          e.printStackTrace();
          Assertions.UNREACHABLE();
          return null;
        }
      }
    }
  }

  /*
   * TODO: cache for efficiency?
   * 
   * @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
   */
  @Override
  public boolean hasLocalVariableTable() {
    try {
      ClassReader.AttrIterator iter = new ClassReader.AttrIterator();
      getCodeReader().initAttributeIterator(iter);
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("LocalVariableTable")) {
          return true;
        }
      }
      return false;
    } catch (InvalidClassFileException e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
      return false;
    }
  }

  private ClassReader getClassReader() {
    return ((ShrikeClass) getDeclaringClass()).getReader();
  }

  private CodeReader getCodeReader() {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the code attribute
    CodeReader code = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("Code")) {
          code = new CodeReader(iter);
          break;
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return code;
  }

  private ExceptionsReader getExceptionReader() {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the desired attribute
    ExceptionsReader result = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("Exceptions")) {
          result = new ExceptionsReader(iter);
          break;
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return result;
  }

  private SignatureReader getSignatureReader() {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the desired attribute
    SignatureReader result = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (iter.getName().equals("Signature")) {
          result = new SignatureReader(iter);
          break;
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return result;
  }

  private AnnotationsReader getAnnotationsReader(boolean runtimeInvisible) {
    ClassReader.AttrIterator iter = new AttrIterator();
    getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);

    // search for the desired attribute
    AnnotationsReader result = null;
    try {
      for (; iter.isValid(); iter.advance()) {
        if (runtimeInvisible) {
          if (iter.getName().equals(RuntimeInvisibleAnnotationsReader.attrName)) {
            result = new RuntimeInvisibleAnnotationsReader(iter);
            break;
          }
        } else {
          if (iter.getName().equals(RuntimeVisibleAnnotationsReader.attrName)) {
            result = new RuntimeVisibleAnnotationsReader(iter);
            break;
          }
        }
      }
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE();
    }
    return result;
  }

  private String computeGenericsSignature() throws InvalidClassFileException {
    SignatureReader reader = getSignatureReader();
    if (reader == null) {
      return null;
    } else {
      return reader.getSignature();
    }
  }

  public TypeReference getReturnType() {
    return getReference().getReturnType();
  }

  public IClassHierarchy getClassHierarchy() {
    return cha;
  }

  /**
   * TODO: cache?
   * 
   * @return raw "Signature" attribute from the bytecode
   * @throws InvalidClassFileException
   */
  private String getGenericsSignature() throws InvalidClassFileException {
    return computeGenericsSignature();
  }

  /**
   * UNDER CONSTRUCTION
   * 
   * @throws InvalidClassFileException
   */
  public MethodTypeSignature getMethodTypeSignature() throws InvalidClassFileException {
    String sig = getGenericsSignature();
    return sig == null ? null : MethodTypeSignature.make(sig);
  }

  /**
   * read the runtime-invisible annotations from the class file
   */
  public Collection getRuntimeInvisibleAnnotations() throws InvalidClassFileException {
    return getAnnotations(true);
  }

  /**
   * read the runtime-visible annotations from the class file
   */
  public Collection getRuntimeVisibleAnnotations() throws InvalidClassFileException {
    return getAnnotations(false);
  }

  public Collection getAnnotations(boolean runtimeInvisible) throws InvalidClassFileException {
    AnnotationsReader r = getAnnotationsReader(runtimeInvisible);
    return Annotation.getAnnotationsFromReader(r, getDeclaringClass().getClassLoader().getReference());
  }


  private static final IndirectionData NO_INDIRECTIONS = new IndirectionData() {

    private final int[] NOTHING = new int[0];
    
    public int[] indirectlyReadLocals(int instructionIndex) {
      return NOTHING;
    }

    public int[] indirectlyWrittenLocals(int instructionIndex) {
      return NOTHING;
    }
    
  };
  
  public IndirectionData getIndirectionData() {
    return NO_INDIRECTIONS;
  }
}
File
ShrikeCTMethod.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.classLoader;

import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.bytecode.BytecodeStream;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.strings.Atom;

/**
 * An implementation of {@link IMethod}, usually for a synthesized method that is not read directly from any source {@link Module}.
 */
public class SyntheticMethod implements IMethod {

  public final static SSAInstruction[] NO_STATEMENTS = new SSAInstruction[0];

  private final MethodReference method;

  protected final IMethod resolvedMethod;

  protected final IClass declaringClass;

  private final boolean isStatic;

  private final boolean isFactory;

  public SyntheticMethod(MethodReference method, IClass declaringClass, boolean isStatic, boolean isFactory) {
    super();
    if (method == null) {
      throw new IllegalArgumentException("null method");
    }
    this.method = method;
    this.resolvedMethod = null;
    this.declaringClass = declaringClass;
    this.isStatic = isStatic;
    this.isFactory = isFactory;
  }

  public SyntheticMethod(IMethod method, IClass declaringClass, boolean isStatic, boolean isFactory) {
    super();
    if (method == null) {
      throw new IllegalArgumentException("null method");
    }
    this.resolvedMethod = method;
    this.method = resolvedMethod.getReference();
    this.declaringClass = declaringClass;
    this.isStatic = isStatic;
    this.isFactory = isFactory;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isClinit()
   */
  public boolean isClinit() {
    return method.getSelector().equals(MethodReference.clinitSelector);
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isInit()
   */
  public boolean isInit() {
    return method.getSelector().equals(MethodReference.initSelector);
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isStatic()
   */
  public boolean isStatic() {
    return isStatic;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isNative()
   */
  public boolean isNative() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isAbstract()
   */
  public boolean isAbstract() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isPrivate()
   */
  public boolean isPrivate() {
    return false;
  }

  public boolean isProtected() {
    return false;
  }

  public boolean isPublic() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isFinal()
   */
  public boolean isFinal() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isBridge()
   */
  public boolean isBridge() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isAbstract()
   */
  public boolean isSynchronized() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isSynthetic()
   */
  public boolean isSynthetic() {
    return true;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#getReference()
   */
  public MethodReference getReference() {
    return method;
  }

  /**
   * Create an {@link InducedCFG} from an instruction array.
   * 
   * NOTE: SIDE EFFECT!!! ... nulls out phi instructions in the instruction array!
   */
  public InducedCFG makeControlFlowGraph(SSAInstruction[] instructions) {
    return new InducedCFG(instructions, this, Everywhere.EVERYWHERE);
  }

  public BytecodeStream getBytecodeStream() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  /*
   * TODO: why isn't this abstract?
   * 
   * @see com.ibm.wala.classLoader.IMethod#getMaxLocals()
   * 
   * @throws UnsupportedOperationException unconditionally
   */
  public int getMaxLocals() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  /*
   * TODO: why isn't this abstract?
   * 
   * @see com.ibm.wala.classLoader.IMethod#getMaxStackHeight()
   */
  public int getMaxStackHeight() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public IClass getDeclaringClass() {
    return declaringClass;
  }

  @Override
  public String toString() {
    StringBuffer s = new StringBuffer("synthetic ");
    if (isFactoryMethod()) {
      s.append(" factory ");
    }
    s.append(method.toString());
    return s.toString();
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode());
  /*
    result = prime * result + ((method == null) ? 0 : method.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    final SyntheticMethod other = (SyntheticMethod) obj;
    if (declaringClass == null) {
      if (other.declaringClass != null)
        return false;
    } else if (!declaringClass.equals(other.declaringClass))
      return false;
    if (method == null) {
      if (other.method != null)
        return false;
    } else if (!method.equals(other.method))
      return false;
    return true;
  }

  public boolean hasExceptionHandler() {
    return false;
  }

  public boolean hasPoison() {
    return false;
  }

  public String getPoison() {
    return null;
  }

  public byte getPoisonLevel() {
    return -1;
  }

  /*
   * TODO: why isn't this abstract?
   * 
   * @param options options governing SSA construction
   */
  @Deprecated
  public SSAInstruction[] getStatements(SSAOptions options) {
    return NO_STATEMENTS;
  }

  /**
   * Most subclasses should override this.
   * 
   * @param context TODO
   * @param options options governing IR conversion
   */
  public IR makeIR(Context context, SSAOptions options) throws UnimplementedError {
    throw new UnimplementedError("haven't implemented IR yet for class " + getClass());
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getParameterType(int)
   */
  public TypeReference getParameterType(int i) {
    if (isStatic()) {
      return method.getParameterType(i);
    } else {
      if (i == 0) {
        return method.getDeclaringClass();
      } else {
        return method.getParameterType(i - 1);
      }
    }
  }

  /**
   * 
   * @see com.ibm.wala.classLoader.IMethod#getNumberOfParameters()
   */
  public int getNumberOfParameters() {
    int n = method.getNumberOfParameters();
    return isStatic() ? n : n + 1;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getDeclaredExceptions()
   */
  public TypeReference[] getDeclaredExceptions() throws InvalidClassFileException {
    if (resolvedMethod == null) {
      return null;
    } else {
      return resolvedMethod.getDeclaredExceptions();
    }
  }

  public Atom getName() {
    return method.getSelector().getName();
  }

  public Descriptor getDescriptor() {
    return method.getSelector().getDescriptor();
  }
/** BEGIN Custom change: : precise bytecode positions */
  
  /*
   * @see com.ibm.wala.classLoader.IMethod#getSourcePosition(int)
   */
  public SourcePosition getSourcePosition(int bcIndex) throws InvalidClassFileException {
    return null;
  }

   * @see com.ibm.wala.classLoader.IMethod#getParameterSourcePosition(int)
   */
  public SourcePosition getParameterSourcePosition(int paramNum) throws InvalidClassFileException {
    return null;
  }
/** END Custom change: precise bytecode positions */

  /*
   * @see com.ibm.wala.classLoader.IMethod#getLineNumber(int)
   */
  public int getLineNumber(int bcIndex) {
    return -1;
  }

  public boolean isFactoryMethod() {
    return isFactory;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSignature()
   */
  public String getSignature() {
    return getReference().getSignature();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSelector()
   */
  public Selector getSelector() {
    return getReference().getSelector();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getLocalVariableName(int, int)
   */
  public String getLocalVariableName(int bcIndex, int localNumber) {
    // no information is available
    return null;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
   */
  public boolean hasLocalVariableTable() {
    return false;
  }

  public SSAInstruction[] getStatements() {
    return getStatements(SSAOptions.defaultOptions());
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getReturnType()
   */
  public TypeReference getReturnType() {
    return getReference().getReturnType();
  }

  public IClassHierarchy getClassHierarchy() {
    return getDeclaringClass().getClassHierarchy();
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.classLoader;

import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.bytecode.BytecodeStream;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.strings.Atom;

/**
 * An implementation of {@link IMethod}, usually for a synthesized method that is not read directly from any source {@link Module}.
 */
public class SyntheticMethod implements IMethod {

  public final static SSAInstruction[] NO_STATEMENTS = new SSAInstruction[0];

  private final MethodReference method;

  protected final IMethod resolvedMethod;

  protected final IClass declaringClass;

  private final boolean isStatic;

  private final boolean isFactory;

  public SyntheticMethod(MethodReference method, IClass declaringClass, boolean isStatic, boolean isFactory) {
    super();
    if (method == null) {
      throw new IllegalArgumentException("null method");
    }
    this.method = method;
    this.resolvedMethod = null;
    this.declaringClass = declaringClass;
    this.isStatic = isStatic;
    this.isFactory = isFactory;
  }

  public SyntheticMethod(IMethod method, IClass declaringClass, boolean isStatic, boolean isFactory) {
    super();
    if (method == null) {
      throw new IllegalArgumentException("null method");
    }
    this.resolvedMethod = method;
    this.method = resolvedMethod.getReference();
    this.declaringClass = declaringClass;
    this.isStatic = isStatic;
    this.isFactory = isFactory;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isClinit()
   */
  public boolean isClinit() {
    return method.getSelector().equals(MethodReference.clinitSelector);
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isInit()
   */
  public boolean isInit() {
    return method.getSelector().equals(MethodReference.initSelector);
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isStatic()
   */
  public boolean isStatic() {
    return isStatic;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isNative()
   */
  public boolean isNative() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isAbstract()
   */
  public boolean isAbstract() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isPrivate()
   */
  public boolean isPrivate() {
    return false;
  }

  public boolean isProtected() {
    return false;
  }

  public boolean isPublic() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isFinal()
   */
  public boolean isFinal() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isBridge()
   */
  public boolean isBridge() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isAbstract()
   */
  public boolean isSynchronized() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isSynthetic()
   */
  public boolean isSynthetic() {
    return true;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#getReference()
   */
  public MethodReference getReference() {
    return method;
  }

  /**
   * Create an {@link InducedCFG} from an instruction array.
   * 
  /*
   * NOTE: SIDE EFFECT!!! ... nulls out phi instructions in the instruction array!
   */
  public InducedCFG makeControlFlowGraph(SSAInstruction[] instructions) {
    return new InducedCFG(instructions, this, Everywhere.EVERYWHERE);
  }

  public BytecodeStream getBytecodeStream() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  /*
   * TODO: why isn't this abstract?
   * 
   * @see com.ibm.wala.classLoader.IMethod#getMaxLocals()
   * 
   * @throws UnsupportedOperationException unconditionally
   */
  public int getMaxLocals() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  /*
   * TODO: why isn't this abstract?
   * 
   * @see com.ibm.wala.classLoader.IMethod#getMaxStackHeight()
   */
  public int getMaxStackHeight() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public IClass getDeclaringClass() {
    return declaringClass;
  }

  @Override
  public String toString() {
    StringBuffer s = new StringBuffer("synthetic ");
    if (isFactoryMethod()) {
      s.append(" factory ");
    }
    s.append(method.toString());
    return s.toString();
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode());
    result = prime * result + ((method == null) ? 0 : method.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    final SyntheticMethod other = (SyntheticMethod) obj;
    if (declaringClass == null) {
      if (other.declaringClass != null)
        return false;
    } else if (!declaringClass.equals(other.declaringClass))
      return false;
    if (method == null) {
      if (other.method != null)
        return false;
    } else if (!method.equals(other.method))
      return false;
    return true;
  }

  public boolean hasExceptionHandler() {
    return false;
  }

  public boolean hasPoison() {
    return false;
  }

  public String getPoison() {
    return null;
  }

  public byte getPoisonLevel() {
    return -1;
  }

  /*
   * TODO: why isn't this abstract?
   * 
   * @param options options governing SSA construction
   */
  @Deprecated
  public SSAInstruction[] getStatements(SSAOptions options) {
    return NO_STATEMENTS;
  }

  /**
   * Most subclasses should override this.
   * 
   * @param context TODO
   * @param options options governing IR conversion
   */
  public IR makeIR(Context context, SSAOptions options) throws UnimplementedError {
    throw new UnimplementedError("haven't implemented IR yet for class " + getClass());
  }

   * @see com.ibm.wala.classLoader.IMethod#getParameterType(int)
   */
  public TypeReference getParameterType(int i) {
    if (isStatic()) {
      return method.getParameterType(i);
    } else {
      if (i == 0) {
        return method.getDeclaringClass();
      } else {
        return method.getParameterType(i - 1);
      }
    }
  }

  /**
   * 
   * @see com.ibm.wala.classLoader.IMethod#getNumberOfParameters()
   */
  public int getNumberOfParameters() {
    int n = method.getNumberOfParameters();
    return isStatic() ? n : n + 1;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getDeclaredExceptions()
   */
  public TypeReference[] getDeclaredExceptions() throws InvalidClassFileException {
    if (resolvedMethod == null) {
      return null;
    } else {
      return resolvedMethod.getDeclaredExceptions();
    }
  }

  public Atom getName() {
    return method.getSelector().getName();
  }

  public Descriptor getDescriptor() {
    return method.getSelector().getDescriptor();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getLineNumber(int)
   */
  public int getLineNumber(int bcIndex) {
    return -1;
  }

  public boolean isFactoryMethod() {
    return isFactory;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSignature()
   */
  public String getSignature() {
    return getReference().getSignature();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSelector()
   */
  public Selector getSelector() {
    return getReference().getSelector();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getLocalVariableName(int, int)
   */
  public String getLocalVariableName(int bcIndex, int localNumber) {
    // no information is available
    return null;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
   */
  public boolean hasLocalVariableTable() {
    return false;
  }

  public SSAInstruction[] getStatements() {
    return getStatements(SSAOptions.defaultOptions());
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getReturnType()
   */
  public TypeReference getReturnType() {
    return getReference().getReturnType();
  }

  public IClassHierarchy getClassHierarchy() {
    return getDeclaringClass().getClassHierarchy();
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
  }
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.classLoader;

import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.bytecode.BytecodeStream;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.strings.Atom;

/**
 * An implementation of {@link IMethod}, usually for a synthesized method that is not read directly from any source {@link Module}.
 */
public class SyntheticMethod implements IMethod {

  public final static SSAInstruction[] NO_STATEMENTS = new SSAInstruction[0];

  private final MethodReference method;

  protected final IMethod resolvedMethod;

  protected final IClass declaringClass;

  private final boolean isStatic;

  private final boolean isFactory;

  public SyntheticMethod(MethodReference method, IClass declaringClass, boolean isStatic, boolean isFactory) {
    super();
    if (method == null) {
      throw new IllegalArgumentException("null method");
    }
    this.method = method;
    this.resolvedMethod = null;
    this.declaringClass = declaringClass;
    this.isStatic = isStatic;
    this.isFactory = isFactory;
  }

  public SyntheticMethod(IMethod method, IClass declaringClass, boolean isStatic, boolean isFactory) {
    super();
    if (method == null) {
      throw new IllegalArgumentException("null method");
    }
    this.resolvedMethod = method;
    this.method = resolvedMethod.getReference();
    this.declaringClass = declaringClass;
    this.isStatic = isStatic;
    this.isFactory = isFactory;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isClinit()
   */
  public boolean isClinit() {
    return method.getSelector().equals(MethodReference.clinitSelector);
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isInit()
   */
  public boolean isInit() {
    return method.getSelector().equals(MethodReference.initSelector);
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isStatic()
   */
  public boolean isStatic() {
    return isStatic;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isNative()
   */
  public boolean isNative() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isAbstract()
   */
  public boolean isAbstract() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isPrivate()
   */
  public boolean isPrivate() {
    return false;
  }

  public boolean isProtected() {
    return false;
  }

  public boolean isPublic() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isFinal()
   */
  public boolean isFinal() {
    return false;

  /**
   * @see com.ibm.wala.classLoader.IMethod#isBridge()
   */
  public boolean isBridge() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isAbstract()
   */
  public boolean isSynchronized() {
    return false;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#isSynthetic()
   */
  public boolean isSynthetic() {
    return true;
  }

  /**
   * @see com.ibm.wala.classLoader.IMethod#getReference()
   */
  public MethodReference getReference() {
    return method;
  }

  /**
   * Create an {@link InducedCFG} from an instruction array.
   * 
   * NOTE: SIDE EFFECT!!! ... nulls out phi instructions in the instruction array!
   */
  public InducedCFG makeControlFlowGraph(SSAInstruction[] instructions) {
    return new InducedCFG(instructions, this, Everywhere.EVERYWHERE);
  }

  public BytecodeStream getBytecodeStream() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  /*
   * TODO: why isn't this abstract?
   * 
   * @see com.ibm.wala.classLoader.IMethod#getMaxLocals()
   * 
   * @throws UnsupportedOperationException unconditionally
   */
  public int getMaxLocals() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  /*
   * TODO: why isn't this abstract?
   * 
   * @see com.ibm.wala.classLoader.IMethod#getMaxStackHeight()
   */
  public int getMaxStackHeight() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public IClass getDeclaringClass() {
    return declaringClass;
  }

  @Override
  public String toString() {
    StringBuffer s = new StringBuffer("synthetic ");
    if (isFactoryMethod()) {
      s.append(" factory ");
    }
    s.append(method.toString());
    return s.toString();
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode());
    result = prime * result + ((method == null) ? 0 : method.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    final SyntheticMethod other = (SyntheticMethod) obj;
    if (declaringClass == null) {
      if (other.declaringClass != null)
        return false;
    } else if (!declaringClass.equals(other.declaringClass))
      return false;
    if (method == null) {
      if (other.method != null)
        return false;
    } else if (!method.equals(other.method))
      return false;
    return true;
  }

  public boolean hasExceptionHandler() {
    return false;
  }

  public boolean hasPoison() {
    return false;
  }

  public String getPoison() {
    return null;
  }

  public byte getPoisonLevel() {
    return -1;
  }

  /*
   * TODO: why isn't this abstract?
   * 
   * @param options options governing SSA construction
   */
  @Deprecated
  public SSAInstruction[] getStatements(SSAOptions options) {
    return NO_STATEMENTS;
  }

  /**
   * Most subclasses should override this.
   * 
   * @param context TODO
   * @param options options governing IR conversion
   */
  public IR makeIR(Context context, SSAOptions options) throws UnimplementedError {
    throw new UnimplementedError("haven't implemented IR yet for class " + getClass());
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getParameterType(int)
   */
  public TypeReference getParameterType(int i) {
    if (isStatic()) {
      return method.getParameterType(i);
    } else {
      if (i == 0) {
        return method.getDeclaringClass();
      } else {
        return method.getParameterType(i - 1);
      }
    }
  }

  /**
   * 
   * @see com.ibm.wala.classLoader.IMethod#getNumberOfParameters()
   */
  public int getNumberOfParameters() {
    int n = method.getNumberOfParameters();
    return isStatic() ? n : n + 1;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getDeclaredExceptions()
   */
  public TypeReference[] getDeclaredExceptions() throws InvalidClassFileException {
    if (resolvedMethod == null) {
      return null;
    } else {
      return resolvedMethod.getDeclaredExceptions();
    }
  }

  public Atom getName() {
    return method.getSelector().getName();
  }

  public Descriptor getDescriptor() {
    return method.getSelector().getDescriptor();
  }
/** BEGIN Custom change: : precise bytecode positions */
  
  /*
   * @see com.ibm.wala.classLoader.IMethod#getSourcePosition(int)
   */
  public SourcePosition getSourcePosition(int bcIndex) throws InvalidClassFileException {
    return null;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getParameterSourcePosition(int)
   */
  public SourcePosition getParameterSourcePosition(int paramNum) throws InvalidClassFileException {
    return null;
  }
/** END Custom change: precise bytecode positions */

  /*
   * @see com.ibm.wala.classLoader.IMethod#getLineNumber(int)
   */
  public int getLineNumber(int bcIndex) {
    return -1;
  }

  public boolean isFactoryMethod() {
    return isFactory;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSignature()
   */
  public String getSignature() {
    return getReference().getSignature();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getSelector()
   */
  public Selector getSelector() {
    return getReference().getSelector();
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getLocalVariableName(int, int)
   */
  public String getLocalVariableName(int bcIndex, int localNumber) {
    // no information is available
    return null;
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
   */
  public boolean hasLocalVariableTable() {
    return false;
  }

  public SSAInstruction[] getStatements() {
    return getStatements(SSAOptions.defaultOptions());
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getReturnType()
   */
  public TypeReference getReturnType() {
    return getReference().getReturnType();
  }

  public IClassHierarchy getClassHierarchy() {
    return getDeclaringClass().getClassHierarchy();
  }

}
File
SyntheticMethod.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph;

import java.io.File;
import java.io.NotSerializableException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import com.ibm.wala.classLoader.ArrayClassLoader;
import com.ibm.wala.classLoader.BinaryDirectoryTreeModule;
import com.ibm.wala.classLoader.ClassFileModule;
import com.ibm.wala.classLoader.IClassLoader;
import com.ibm.wala.classLoader.JarFileModule;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.SourceDirectoryTreeModule;
import com.ibm.wala.classLoader.SourceFileModule;
import com.ibm.wala.ipa.callgraph.impl.SetOfClasses;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.PlatformUtil;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.strings.ImmutableByteArray;

/**
 * Base class that represents a set of files to analyze.
 * 
 * The analysis scope is partitioned by class loader. There are three pre-defined class loader scopes:
 * 
    *
  • Primordial (for rt.jar, the core classes) *
  • Extension (for extension libraries in $JRE/lib/ext) *
  • Application (for the classes of the application) *
* * Each class loader will load a set of classes described by a {@link Module}. */ public class AnalysisScope { private final static int DEBUG_LEVEL = 0; public static final Atom PRIMORDIAL = Atom.findOrCreateUnicodeAtom("Primordial"); public static final Atom EXTENSION = Atom.findOrCreateUnicodeAtom("Extension"); public static final Atom APPLICATION = Atom.findOrCreateUnicodeAtom("Application"); public static final Atom SYNTHETIC = Atom.findOrCreateUnicodeAtom("Synthetic"); /** * Create an analysis scope initialized for analysis of Java */ public static AnalysisScope createJavaAnalysisScope() { AnalysisScope scope = new AnalysisScope(Collections.singleton(Language.JAVA)); scope.initForJava(); return scope; } /** * Initialize a scope for java analysis */ protected void initForJava() { initCoreForJava(); initSynthetic(loadersByName.get(APPLICATION)); } /** * Initialize the standard 3 class loaders for java analysis */ protected void initCoreForJava() { ClassLoaderReference primordial = new ClassLoaderReference(PRIMORDIAL, ClassLoaderReference.Java, null); ClassLoaderReference extension = new ClassLoaderReference(EXTENSION, ClassLoaderReference.Java, primordial); ClassLoaderReference application = new ClassLoaderReference(APPLICATION, ClassLoaderReference.Java, extension); loadersByName.put(PRIMORDIAL, primordial); loadersByName.put(EXTENSION, extension); loadersByName.put(APPLICATION, application); } /** * Create the class loader for synthetic classes. */ protected void initSynthetic(ClassLoaderReference parent) { ClassLoaderReference synthetic = new ClassLoaderReference(SYNTHETIC, ClassLoaderReference.Java, parent); setLoaderImpl(synthetic, "com.ibm.wala.ipa.summaries.BypassSyntheticClassLoader"); loadersByName.put(SYNTHETIC, synthetic); } /** * A set of classes to exclude from the analysis entirely. */ private SetOfClasses exclusions; final protected LinkedHashMap loadersByName = new LinkedHashMap(); /** * Special class loader for array instances */ private final ArrayClassLoader arrayClassLoader = new ArrayClassLoader(); final private Map> moduleMap = HashMapFactory.make(3); private final Map languages; protected AnalysisScope(Collection languages) { super(); this.languages = new HashMap(); for (Language l : languages) { this.languages.put(l.getName(), l); } } public Language getLanguage(Atom name) { return languages.get(name); } public boolean isApplicationLoader(IClassLoader loader) { return loader.getReference().equals(getLoader(APPLICATION)); } /** * Return the information regarding the primordial loader. */ public ClassLoaderReference getPrimordialLoader() { return getLoader(PRIMORDIAL); } /** * Return the information regarding the extension loader. */ public ClassLoaderReference getExtensionLoader() { return getLoader(EXTENSION); } /** * Return the information regarding the application loader. */ public ClassLoaderReference getApplicationLoader() { return getLoader(APPLICATION); } /** * Return the information regarding the application loader. */ public ClassLoaderReference getSyntheticLoader() { return getLoader(SYNTHETIC); } /** * @return the set of languages to be processed during this analysis session. */ public Collection getLanguages() { return languages.values(); } /** * @return the set of "base languages," each of which defines a family of compatible languages, and therefore induces a distinct * ClassHierarchy */ public Set getBaseLanguages() { Set result = HashSetFactory.make(); for (Language language : getLanguages()) { if (language.getBaseLanguage() == null) { result.add(language); } } return result; } /** * Add a class file to the scope for a loader */ public void addSourceFileToScope(ClassLoaderReference loader, File file, String fileName) throws IllegalArgumentException { List s = MapUtil.findOrCreateList(moduleMap, loader); s.add(new SourceFileModule(file, fileName)); } /** * Add a class file to the scope for a loader * @throws InvalidClassFileException */ public void addClassFileToScope(ClassLoaderReference loader, File file) throws IllegalArgumentException, InvalidClassFileException { List s = MapUtil.findOrCreateList(moduleMap, loader); s.add(new ClassFileModule(file)); } /** * Add a jar file to the scope for a loader */ public void addToScope(ClassLoaderReference loader, JarFile file) { List s = MapUtil.findOrCreateList(moduleMap, loader); if (DEBUG_LEVEL > 0) { System.err.println(("AnalysisScope: add JarFileModule " + file.getName())); } s.add(new JarFileModule(file)); } /** * Add a module to the scope for a loader */ public void addToScope(ClassLoaderReference loader, Module m) { if (m == null) { throw new IllegalArgumentException("null m"); } List s = MapUtil.findOrCreateList(moduleMap, loader); if (DEBUG_LEVEL > 0) { System.err.println(("AnalysisScope: add module " + m)); } s.add(m); } /** * Add all modules from another scope */ public void addToScope(AnalysisScope other) { if (other == null) { throw new IllegalArgumentException("null other"); } for (ClassLoaderReference loader : other.getLoaders()) { for (Module m : other.getModules(loader)) { addToScope(loader, m); } } } /** * Add a module file to the scope for a loader. The classes in the added jar file will override classes added to the scope so far. */ public void addToScopeHead(ClassLoaderReference loader, Module m) { if (m == null) { throw new IllegalArgumentException("null m"); } List s = MapUtil.findOrCreateList(moduleMap, loader); if (DEBUG_LEVEL > 0) { System.err.println(("AnalysisScope: add overriding module " + m)); } s.add(0, m); } /** * @return the ClassLoaderReference specified by name. * @throws IllegalArgumentException if name is null */ public ClassLoaderReference getLoader(Atom name) throws IllegalArgumentException { if (name == null) { throw new IllegalArgumentException("name is null"); } if (name.length() == 0) { throw new IllegalArgumentException("empty atom is not a legal class loader name"); } /* * if (Assertions.verifyAssertions) { if (name.getVal(0) > 'Z') { Assertions._assert(name.getVal(0) <= 'Z', * "Classloader name improperly capitalised? (" + name + ")"); } } */ return loadersByName.get(name); } protected ClassLoaderReference classLoaderName2Ref(String clName) { return getLoader(Atom.findOrCreateUnicodeAtom(clName)); } private final HashMap loaderImplByRef = HashMapFactory.make(); public String getLoaderImpl(ClassLoaderReference ref) { return loaderImplByRef.get(ref); } public void setLoaderImpl(ClassLoaderReference ref, String implClass) { if (ref == null) { throw new IllegalArgumentException("null ref"); } if (implClass == null) { throw new IllegalArgumentException("null implClass"); } loaderImplByRef.put(ref, implClass); } public Collection getLoaders() { return Collections.unmodifiableCollection(loadersByName.values()); } public int getNumberOfLoaders() { return loadersByName.values().size(); } public SetOfClasses getExclusions() { return exclusions; } public void setExclusions(SetOfClasses classes) { exclusions = classes; } @Override public String toString() { StringBuffer result = new StringBuffer(); for (ClassLoaderReference loader : loadersByName.values()) { result.append(loader.getName()); result.append("\n"); for (Module m : getModules(loader)) { result.append(" "); result.append(m); result.append("\n"); } } result.append(getExclusionString()); result.append("\n"); return result.toString(); } /** * @return a String that describes exclusions from the analysis scope. */ protected Object getExclusionString() { return "Exclusions: " + exclusions; } /** * Utility function. Useful when parsing input. */ public MethodReference findMethod(Atom loader, String klass, Atom name, ImmutableByteArray desc) { if (desc == null) { throw new IllegalArgumentException("null desc"); } ClassLoaderReference clr = getLoader(loader); Descriptor ddesc = Descriptor.findOrCreate(languages.get(clr.getLanguage()), desc); TypeReference type = TypeReference.findOrCreate(clr, TypeName.string2TypeName(klass)); return MethodReference.findOrCreate(type, name, ddesc); } public List getModules(ClassLoaderReference loader) { List result = moduleMap.get(loader); * List empty = Collections.emptyList(); return result == null ? empty : result; } /** * @return Returns the arrayClassLoader. */ public ArrayClassLoader getArrayClassLoader() { return arrayClassLoader; } /** * @return the rt.jar (1.4) or core.jar (1.5) file, or null if not found. */ private JarFile getRtJar() { for (Iterator MS = getModules(getPrimordialLoader()).iterator(); MS.hasNext();) { Module M = (Module) MS.next(); if (M instanceof JarFileModule) { JarFile JF = ((JarFileModule) M).getJarFile(); if (JF.getName().endsWith(File.separator + "rt.jar")) { return JF; } if (JF.getName().endsWith(File.separator + "core.jar")) { return JF; } // hack for Mac if (PlatformUtil.onMacOSX() && JF.getName().endsWith(File.separator + "classes.jar")) { return JF; } } } return null; } public String getJavaLibraryVersion() throws IllegalStateException { JarFile rtJar = getRtJar(); if (rtJar == null) { throw new IllegalStateException("cannot find runtime libraries"); } try { Manifest man = rtJar.getManifest(); assert man != null : "runtime library has no manifest!"; String result = man.getMainAttributes().getValue("Specification-Version"); if (result == null) { Attributes att = man.getMainAttributes(); System.err.println("main attributes:" + att); Assertions.UNREACHABLE("Manifest for " + rtJar.getName() + " has no value for Specification-Version"); } return result; } catch (java.io.IOException e) { Assertions.UNREACHABLE("error getting rt.jar manifest!"); return null; } } public boolean isJava17Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.7"); } import java.io.NotSerializableException; public boolean isJava16Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.6"); } public boolean isJava15Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.5"); } public boolean isJava14Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.4"); } /** * Creates a "serializable" version of the analysis scope. * * @return a "serializable" version of the analysis scope. * @throws NotSerializableException */ public ShallowAnalysisScope toShallowAnalysisScope() throws NotSerializableException { if (getArrayClassLoader().getNumberOfClasses() != 0) { throw new NotSerializableException("Scope was already used for building array classes"); } // Note: 'arrayClassLoader' object will be built from scratch in remote process // represent modules map as a set of strings (corresponding to analysis scope file lines. List moduleLines = new ArrayList(); for (Map.Entry> e : moduleMap.entrySet()) { ClassLoaderReference lrReference = e.getKey(); String moduleLdr = lrReference.getName().toString(); String moduleLang = lrReference.getLanguage().toString(); assert Language.JAVA.getName().equals(lrReference.getLanguage()) : "Java language only is currently supported"; for (Module m : e.getValue()) { String moduleType; String modulePath; if (m instanceof JarFileModule) { moduleType = "jarFile"; modulePath = ((JarFileModule) m).getAbsolutePath(); } else if (m instanceof BinaryDirectoryTreeModule) { moduleType = "binaryDir"; modulePath = ((BinaryDirectoryTreeModule) m).getPath(); } else if (m instanceof SourceDirectoryTreeModule) { moduleType = "sourceDir"; modulePath = ((SourceDirectoryTreeModule) m).getPath(); } else if (m instanceof SourceFileModule) { moduleType = "sourceFile"; modulePath = ((SourceFileModule) m).getAbsolutePath(); } else { Assertions.UNREACHABLE("Module type isn't supported - " + m); continue; } modulePath.replace("\\", "/"); String moduleDescrLine = String.format("%s,%s,%s,%s", moduleLdr, moduleLang, moduleType, modulePath); moduleLines.add(moduleDescrLine); } } // represent loaderImplByRef map as set of strings List ldrImplLines = new ArrayList(); for (Map.Entry e : loaderImplByRef.entrySet()) { ClassLoaderReference lrReference = e.getKey(); String ldrName = lrReference.getName().toString(); String ldrLang = lrReference.getLanguage().toString(); assert Language.JAVA.getName().equals(lrReference.getLanguage()) : "Java language only is currently supported"; String ldrImplName = e.getValue(); String ldrImplDescrLine = String.format("%s,%s,%s,%s", ldrName, ldrLang, "loaderImpl", ldrImplName); ldrImplLines.add(ldrImplDescrLine); } ShallowAnalysisScope shallowScope = new ShallowAnalysisScope(getExclusions(), moduleLines, ldrImplLines); return shallowScope; } } ======= /******************************************************************************* * Copyright (c) 2002 - 2006 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.ipa.callgraph; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; import com.ibm.wala.classLoader.ArrayClassLoader; import com.ibm.wala.classLoader.BinaryDirectoryTreeModule; import com.ibm.wala.classLoader.ClassFileModule; import com.ibm.wala.classLoader.IClassLoader; import com.ibm.wala.classLoader.JarFileModule; import com.ibm.wala.classLoader.Language; import com.ibm.wala.classLoader.Module; import com.ibm.wala.classLoader.SourceDirectoryTreeModule; import com.ibm.wala.classLoader.SourceFileModule; import com.ibm.wala.ipa.callgraph.impl.SetOfClasses; import com.ibm.wala.shrikeCT.InvalidClassFileException; import com.ibm.wala.types.ClassLoaderReference; import com.ibm.wala.types.Descriptor; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeName; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.PlatformUtil; import com.ibm.wala.util.collections.HashMapFactory; import com.ibm.wala.util.collections.HashSetFactory; import com.ibm.wala.util.collections.MapUtil; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.strings.Atom; import com.ibm.wala.util.strings.ImmutableByteArray; /** * Base class that represents a set of files to analyze. * The analysis scope is partitioned by class loader. There are three pre-defined class loader scopes: *
    *
  • Primordial (for rt.jar, the core classes) *
  • Extension (for extension libraries in $JRE/lib/ext) *
  • Application (for the classes of the application) *
* * Each class loader will load a set of classes described by a {@link Module}. */ public class AnalysisScope { private final static int DEBUG_LEVEL = 0; public static final Atom PRIMORDIAL = Atom.findOrCreateUnicodeAtom("Primordial"); public static final Atom EXTENSION = Atom.findOrCreateUnicodeAtom("Extension"); public static final Atom APPLICATION = Atom.findOrCreateUnicodeAtom("Application"); public static final Atom SYNTHETIC = Atom.findOrCreateUnicodeAtom("Synthetic"); /** * Create an analysis scope initialized for analysis of Java */ public static AnalysisScope createJavaAnalysisScope() { AnalysisScope scope = new AnalysisScope(Collections.singleton(Language.JAVA)); scope.initForJava(); return scope; } /** * Initialize a scope for java analysis */ protected void initForJava() { initCoreForJava(); initSynthetic(loadersByName.get(APPLICATION)); } /** * Initialize the standard 3 class loaders for java analysis */ protected void initCoreForJava() { ClassLoaderReference primordial = new ClassLoaderReference(PRIMORDIAL, ClassLoaderReference.Java, null); ClassLoaderReference extension = new ClassLoaderReference(EXTENSION, ClassLoaderReference.Java, primordial); ClassLoaderReference application = new ClassLoaderReference(APPLICATION, ClassLoaderReference.Java, extension); loadersByName.put(PRIMORDIAL, primordial); loadersByName.put(EXTENSION, extension); loadersByName.put(APPLICATION, application); } /** * Create the class loader for synthetic classes. */ protected void initSynthetic(ClassLoaderReference parent) { ClassLoaderReference synthetic = new ClassLoaderReference(SYNTHETIC, ClassLoaderReference.Java, parent); @Override setLoaderImpl(synthetic, "com.ibm.wala.ipa.summaries.BypassSyntheticClassLoader"); loadersByName.put(SYNTHETIC, synthetic); } /** * A set of classes to exclude from the analysis entirely. */ private SetOfClasses exclusions; final protected LinkedHashMap loadersByName = new LinkedHashMap(); /** * Special class loader for array instances */ private final ArrayClassLoader arrayClassLoader = new ArrayClassLoader(); final private Map> moduleMap = HashMapFactory.make(3); private final Map languages; protected AnalysisScope(Collection languages) { super(); this.languages = new HashMap(); for (Language l : languages) { this.languages.put(l.getName(), l); } } public Language getLanguage(Atom name) { return languages.get(name); } public boolean isApplicationLoader(IClassLoader loader) { return loader.getReference().equals(getLoader(APPLICATION)); } /** * Return the information regarding the primordial loader. */ public ClassLoaderReference getPrimordialLoader() { return getLoader(PRIMORDIAL); } /** * Return the information regarding the extension loader. */ public ClassLoaderReference getExtensionLoader() { return getLoader(EXTENSION); } /** * Return the information regarding the application loader. */ public ClassLoaderReference getApplicationLoader() { return getLoader(APPLICATION); } /** * Return the information regarding the application loader. */ public ClassLoaderReference getSyntheticLoader() { return getLoader(SYNTHETIC); } /** * @return the set of languages to be processed during this analysis session. */ public Collection getLanguages() { return languages.values(); } /** * @return the set of "base languages," each of which defines a family of compatible languages, and therefore induces a distinct * ClassHierarchy */ public Set getBaseLanguages() { Set result = HashSetFactory.make(); for (Language language : getLanguages()) { if (language.getBaseLanguage() == null) { result.add(language); } } return result; } /** * Add a class file to the scope for a loader */ public void addSourceFileToScope(ClassLoaderReference loader, File file, String fileName) throws IllegalArgumentException { List s = MapUtil.findOrCreateList(moduleMap, loader); s.add(new SourceFileModule(file, fileName)); } /** * Add a class file to the scope for a loader * @throws InvalidClassFileException */ public void addClassFileToScope(ClassLoaderReference loader, File file) throws IllegalArgumentException, InvalidClassFileException { List s = MapUtil.findOrCreateList(moduleMap, loader); s.add(new ClassFileModule(file)); } /** * Add a jar file to the scope for a loader */ public String toString() { public void addToScope(ClassLoaderReference loader, JarFile file) { List s = MapUtil.findOrCreateList(moduleMap, loader); if (DEBUG_LEVEL > 0) { System.err.println(("AnalysisScope: add JarFileModule " + file.getName())); } s.add(new JarFileModule(file)); } /** * Add a module to the scope for a loader */ public void addToScope(ClassLoaderReference loader, Module m) { if (m == null) { throw new IllegalArgumentException("null m"); } List s = MapUtil.findOrCreateList(moduleMap, loader); if (DEBUG_LEVEL > 0) { System.err.println(("AnalysisScope: add module " + m)); } s.add(m); } /** * Add all modules from another scope */ public void addToScope(AnalysisScope other) { if (other == null) { throw new IllegalArgumentException("null other"); } for (ClassLoaderReference loader : other.getLoaders()) { for (Module m : other.getModules(loader)) { addToScope(loader, m); } } } /** * Add a module file to the scope for a loader. The classes in the added jar file will override classes added to the scope so far. */ public void addToScopeHead(ClassLoaderReference loader, Module m) { if (m == null) { throw new IllegalArgumentException("null m"); } List s = MapUtil.findOrCreateList(moduleMap, loader); if (DEBUG_LEVEL > 0) { System.err.println(("AnalysisScope: add overriding module " + m)); } s.add(0, m); } /** * @return the ClassLoaderReference specified by name. * @throws IllegalArgumentException if name is null */ public ClassLoaderReference getLoader(Atom name) throws IllegalArgumentException { if (name == null) { throw new IllegalArgumentException("name is null"); } if (name.length() == 0) { throw new IllegalArgumentException("empty atom is not a legal class loader name"); } /* * if (Assertions.verifyAssertions) { if (name.getVal(0) > 'Z') { Assertions._assert(name.getVal(0) <= 'Z', * "Classloader name improperly capitalised? (" + name + ")"); } } */ return loadersByName.get(name); } protected ClassLoaderReference classLoaderName2Ref(String clName) { return getLoader(Atom.findOrCreateUnicodeAtom(clName)); } private final HashMap loaderImplByRef = HashMapFactory.make(); public String getLoaderImpl(ClassLoaderReference ref) { return loaderImplByRef.get(ref); } public void setLoaderImpl(ClassLoaderReference ref, String implClass) { if (ref == null) { throw new IllegalArgumentException("null ref"); } if (implClass == null) { throw new IllegalArgumentException("null implClass"); } loaderImplByRef.put(ref, implClass); } public Collection getLoaders() { return Collections.unmodifiableCollection(loadersByName.values()); } public int getNumberOfLoaders() { return loadersByName.values().size(); } public SetOfClasses getExclusions() { return exclusions; } public void setExclusions(SetOfClasses classes) { exclusions = classes; } StringBuffer result = new StringBuffer(); for (ClassLoaderReference loader : loadersByName.values()) { result.append(loader.getName()); result.append("\n"); for (Module m : getModules(loader)) { result.append(" "); result.append(m); result.append("\n"); } } result.append(getExclusionString()); result.append("\n"); return result.toString(); } /** * @return a String that describes exclusions from the analysis scope. */ protected Object getExclusionString() { return "Exclusions: " + exclusions; } /** * Utility function. Useful when parsing input. */ public MethodReference findMethod(Atom loader, String klass, Atom name, ImmutableByteArray desc) { if (desc == null) { throw new IllegalArgumentException("null desc"); } ClassLoaderReference clr = getLoader(loader); Descriptor ddesc = Descriptor.findOrCreate(languages.get(clr.getLanguage()), desc); TypeReference type = TypeReference.findOrCreate(clr, TypeName.string2TypeName(klass)); return MethodReference.findOrCreate(type, name, ddesc); } public List getModules(ClassLoaderReference loader) { List result = moduleMap.get(loader); List empty = Collections.emptyList(); return result == null ? empty : result; } /** * @return Returns the arrayClassLoader. */ public ArrayClassLoader getArrayClassLoader() { return arrayClassLoader; } /** * @return the rt.jar (1.4) or core.jar (1.5) file, or null if not found. */ private JarFile getRtJar() { for (Iterator MS = getModules(getPrimordialLoader()).iterator(); MS.hasNext();) { Module M = (Module) MS.next(); if (M instanceof JarFileModule) { JarFile JF = ((JarFileModule) M).getJarFile(); if (JF.getName().endsWith(File.separator + "rt.jar")) { return JF; } if (JF.getName().endsWith(File.separator + "core.jar")) { return JF; } // hack for Mac if (PlatformUtil.onMacOSX() && JF.getName().endsWith(File.separator + "classes.jar")) { return JF; } } } return null; } public String getJavaLibraryVersion() throws IllegalStateException { JarFile rtJar = getRtJar(); if (rtJar == null) { throw new IllegalStateException("cannot find runtime libraries"); } try { Manifest man = rtJar.getManifest(); assert man != null : "runtime library has no manifest!"; String result = man.getMainAttributes().getValue("Specification-Version"); if (result == null) { Attributes att = man.getMainAttributes(); System.err.println("main attributes:" + att); Assertions.UNREACHABLE("Manifest for " + rtJar.getName() + " has no value for Specification-Version"); } return result; } catch (java.io.IOException e) { Assertions.UNREACHABLE("error getting rt.jar manifest!"); return null; } } public boolean isJava17Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.7"); } public boolean isJava16Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.6"); } public boolean isJava15Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.5"); } public boolean isJava14Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.4"); } /** * Creates a "serializable" version of the analysis scope. * * @return a "serializable" version of the analysis scope. * @throws NotSerializableException */ public ShallowAnalysisScope toShallowAnalysisScope() throws NotSerializableException { if (getArrayClassLoader().getNumberOfClasses() != 0) { throw new NotSerializableException("Scope was already used for building array classes"); } // Note: 'arrayClassLoader' object will be built from scratch in remote process // represent modules map as a set of strings (corresponding to analysis scope file lines. List moduleLines = new ArrayList(); for (Map.Entry> e : moduleMap.entrySet()) { ClassLoaderReference lrReference = e.getKey(); String moduleLdr = lrReference.getName().toString(); String moduleLang = lrReference.getLanguage().toString(); assert Language.JAVA.getName().equals(lrReference.getLanguage()) : "Java language only is currently supported"; for (Module m : e.getValue()) { String moduleType; String modulePath; if (m instanceof JarFileModule) { moduleType = "jarFile"; modulePath = ((JarFileModule) m).getAbsolutePath(); } else if (m instanceof BinaryDirectoryTreeModule) { moduleType = "binaryDir"; modulePath = ((BinaryDirectoryTreeModule) m).getPath(); } else if (m instanceof SourceDirectoryTreeModule) { moduleType = "sourceDir"; modulePath = ((SourceDirectoryTreeModule) m).getPath(); } else if (m instanceof SourceFileModule) { moduleType = "sourceFile"; modulePath = ((SourceFileModule) m).getAbsolutePath(); } else { Assertions.UNREACHABLE("Module type isn't supported - " + m); continue; } modulePath.replace("\\", "/"); String moduleDescrLine = String.format("%s,%s,%s,%s", moduleLdr, moduleLang, moduleType, modulePath); moduleLines.add(moduleDescrLine); } } // represent loaderImplByRef map as set of strings List ldrImplLines = new ArrayList(); for (Map.Entry e : loaderImplByRef.entrySet()) { ClassLoaderReference lrReference = e.getKey(); String ldrName = lrReference.getName().toString(); String ldrLang = lrReference.getLanguage().toString(); assert Language.JAVA.getName().equals(lrReference.getLanguage()) : "Java language only is currently supported"; String ldrImplName = e.getValue(); String ldrImplDescrLine = String.format("%s,%s,%s,%s", ldrName, ldrLang, "loaderImpl", ldrImplName); ldrImplLines.add(ldrImplDescrLine); } ShallowAnalysisScope shallowScope = new ShallowAnalysisScope(getExclusions(), moduleLines, ldrImplLines); return shallowScope; } } >>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph;

import java.io.File;
import java.io.NotSerializableException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import com.ibm.wala.classLoader.ArrayClassLoader;
import com.ibm.wala.classLoader.BinaryDirectoryTreeModule;
import com.ibm.wala.classLoader.ClassFileModule;
import com.ibm.wala.classLoader.IClassLoader;
import com.ibm.wala.classLoader.JarFileModule;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.SourceDirectoryTreeModule;
import com.ibm.wala.classLoader.SourceFileModule;
import com.ibm.wala.ipa.callgraph.impl.SetOfClasses;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.PlatformUtil;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.strings.ImmutableByteArray;

/**
 * Base class that represents a set of files to analyze.
 * 
 * The analysis scope is partitioned by class loader. There are three pre-defined class loader scopes:
 * 
    *
  • Primordial (for rt.jar, the core classes) *
  • Extension (for extension libraries in $JRE/lib/ext) *
  • Application (for the classes of the application) *
* * Each class loader will load a set of classes described by a {@link Module}. */ public class AnalysisScope { private final static int DEBUG_LEVEL = 0; */ public static final Atom PRIMORDIAL = Atom.findOrCreateUnicodeAtom("Primordial"); public static final Atom EXTENSION = Atom.findOrCreateUnicodeAtom("Extension"); public static final Atom APPLICATION = Atom.findOrCreateUnicodeAtom("Application"); public static final Atom SYNTHETIC = Atom.findOrCreateUnicodeAtom("Synthetic"); /** * Create an analysis scope initialized for analysis of Java */ public static AnalysisScope createJavaAnalysisScope() { AnalysisScope scope = new AnalysisScope(Collections.singleton(Language.JAVA)); scope.initForJava(); return scope; } /** * Initialize a scope for java analysis */ protected void initForJava() { initCoreForJava(); initSynthetic(loadersByName.get(APPLICATION)); } /** * Initialize the standard 3 class loaders for java analysis */ protected void initCoreForJava() { ClassLoaderReference primordial = new ClassLoaderReference(PRIMORDIAL, ClassLoaderReference.Java, null); ClassLoaderReference extension = new ClassLoaderReference(EXTENSION, ClassLoaderReference.Java, primordial); ClassLoaderReference application = new ClassLoaderReference(APPLICATION, ClassLoaderReference.Java, extension); loadersByName.put(PRIMORDIAL, primordial); loadersByName.put(EXTENSION, extension); loadersByName.put(APPLICATION, application); } /** * Create the class loader for synthetic classes. */ protected void initSynthetic(ClassLoaderReference parent) { ClassLoaderReference synthetic = new ClassLoaderReference(SYNTHETIC, ClassLoaderReference.Java, parent); setLoaderImpl(synthetic, "com.ibm.wala.ipa.summaries.BypassSyntheticClassLoader"); loadersByName.put(SYNTHETIC, synthetic); } /** * A set of classes to exclude from the analysis entirely. */ private SetOfClasses exclusions; final protected LinkedHashMap loadersByName = new LinkedHashMap(); /** * Special class loader for array instances */ private final ArrayClassLoader arrayClassLoader = new ArrayClassLoader(); final private Map> moduleMap = HashMapFactory.make(3); private final Map languages; protected AnalysisScope(Collection languages) { super(); this.languages = new HashMap(); for (Language l : languages) { this.languages.put(l.getName(), l); } } public Language getLanguage(Atom name) { return languages.get(name); } public boolean isApplicationLoader(IClassLoader loader) { return loader.getReference().equals(getLoader(APPLICATION)); } /** * Return the information regarding the primordial loader. */ public ClassLoaderReference getPrimordialLoader() { return getLoader(PRIMORDIAL); } /** * Return the information regarding the extension loader. */ public ClassLoaderReference getExtensionLoader() { return getLoader(EXTENSION); } /** * Return the information regarding the application loader. */ public ClassLoaderReference getApplicationLoader() { return getLoader(APPLICATION); } /** * Return the information regarding the application loader. public ClassLoaderReference getSyntheticLoader() { return getLoader(SYNTHETIC); } /** * @return the set of languages to be processed during this analysis session. */ public Collection getLanguages() { return languages.values(); } /** * @return the set of "base languages," each of which defines a family of compatible languages, and therefore induces a distinct * ClassHierarchy */ public Set getBaseLanguages() { Set result = HashSetFactory.make(); for (Language language : getLanguages()) { if (language.getBaseLanguage() == null) { result.add(language); } } return result; } /** * Add a class file to the scope for a loader */ public void addSourceFileToScope(ClassLoaderReference loader, File file, String fileName) throws IllegalArgumentException { List s = MapUtil.findOrCreateList(moduleMap, loader); s.add(new SourceFileModule(file, fileName)); } /** * Add a class file to the scope for a loader * @throws InvalidClassFileException */ public void addClassFileToScope(ClassLoaderReference loader, File file) throws IllegalArgumentException, InvalidClassFileException { List s = MapUtil.findOrCreateList(moduleMap, loader); s.add(new ClassFileModule(file)); } /** * Add a jar file to the scope for a loader */ public void addToScope(ClassLoaderReference loader, JarFile file) { List s = MapUtil.findOrCreateList(moduleMap, loader); if (DEBUG_LEVEL > 0) { System.err.println(("AnalysisScope: add JarFileModule " + file.getName())); } s.add(new JarFileModule(file)); } /** * Add a module to the scope for a loader */ public void addToScope(ClassLoaderReference loader, Module m) { if (m == null) { throw new IllegalArgumentException("null m"); } List s = MapUtil.findOrCreateList(moduleMap, loader); if (DEBUG_LEVEL > 0) { System.err.println(("AnalysisScope: add module " + m)); } s.add(m); } /** * Add all modules from another scope */ public void addToScope(AnalysisScope other) { if (other == null) { throw new IllegalArgumentException("null other"); } for (ClassLoaderReference loader : other.getLoaders()) { for (Module m : other.getModules(loader)) { addToScope(loader, m); } } } /** * Add a module file to the scope for a loader. The classes in the added jar file will override classes added to the scope so far. */ public void addToScopeHead(ClassLoaderReference loader, Module m) { if (m == null) { throw new IllegalArgumentException("null m"); } List s = MapUtil.findOrCreateList(moduleMap, loader); if (DEBUG_LEVEL > 0) { System.err.println(("AnalysisScope: add overriding module " + m)); } s.add(0, m); } /** * @return the ClassLoaderReference specified by name. * @throws IllegalArgumentException if name is null */ public ClassLoaderReference getLoader(Atom name) throws IllegalArgumentException { if (name == null) { throw new IllegalArgumentException("name is null"); } if (name.length() == 0) { throw new IllegalArgumentException("empty atom is not a legal class loader name"); } /* * if (Assertions.verifyAssertions) { if (name.getVal(0) > 'Z') { Assertions._assert(name.getVal(0) <= 'Z', * "Classloader name improperly capitalised? (" + name + ")"); } } */ return loadersByName.get(name); } protected ClassLoaderReference classLoaderName2Ref(String clName) { return getLoader(Atom.findOrCreateUnicodeAtom(clName)); } private final HashMap loaderImplByRef = HashMapFactory.make(); public String getLoaderImpl(ClassLoaderReference ref) { return loaderImplByRef.get(ref); } public void setLoaderImpl(ClassLoaderReference ref, String implClass) { if (ref == null) { throw new IllegalArgumentException("null ref"); } if (implClass == null) { throw new IllegalArgumentException("null implClass"); } loaderImplByRef.put(ref, implClass); } public Collection getLoaders() { return Collections.unmodifiableCollection(loadersByName.values()); } public int getNumberOfLoaders() { return loadersByName.values().size(); } public SetOfClasses getExclusions() { return exclusions; } public void setExclusions(SetOfClasses classes) { exclusions = classes; } @Override public String toString() { StringBuffer result = new StringBuffer(); for (ClassLoaderReference loader : loadersByName.values()) { result.append(loader.getName()); result.append("\n"); for (Module m : getModules(loader)) { result.append(" "); result.append(m); result.append("\n"); } } result.append(getExclusionString()); result.append("\n"); return result.toString(); } /** * @return a String that describes exclusions from the analysis scope. */ protected Object getExclusionString() { return "Exclusions: " + exclusions; } /** * Utility function. Useful when parsing input. */ public MethodReference findMethod(Atom loader, String klass, Atom name, ImmutableByteArray desc) { if (desc == null) { throw new IllegalArgumentException("null desc"); } ClassLoaderReference clr = getLoader(loader); Descriptor ddesc = Descriptor.findOrCreate(languages.get(clr.getLanguage()), desc); TypeReference type = TypeReference.findOrCreate(clr, TypeName.string2TypeName(klass)); return MethodReference.findOrCreate(type, name, ddesc); } public List getModules(ClassLoaderReference loader) { List result = moduleMap.get(loader); List empty = Collections.emptyList(); return result == null ? empty : result; } /** * @return Returns the arrayClassLoader. */ public ArrayClassLoader getArrayClassLoader() { return arrayClassLoader; } /** * @return the rt.jar (1.4) or core.jar (1.5) file, or null if not found. */ private JarFile getRtJar() { for (Iterator MS = getModules(getPrimordialLoader()).iterator(); MS.hasNext();) { Module M = (Module) MS.next(); if (M instanceof JarFileModule) { JarFile JF = ((JarFileModule) M).getJarFile(); if (JF.getName().endsWith(File.separator + "rt.jar")) { return JF; } if (JF.getName().endsWith(File.separator + "core.jar")) { return JF; } // hack for Mac if (PlatformUtil.onMacOSX() && JF.getName().endsWith(File.separator + "classes.jar")) { return JF; } } } return null; } public String getJavaLibraryVersion() throws IllegalStateException { JarFile rtJar = getRtJar(); if (rtJar == null) { throw new IllegalStateException("cannot find runtime libraries"); } try { Manifest man = rtJar.getManifest(); assert man != null : "runtime library has no manifest!"; String result = man.getMainAttributes().getValue("Specification-Version"); if (result == null) { Attributes att = man.getMainAttributes(); System.err.println("main attributes:" + att); Assertions.UNREACHABLE("Manifest for " + rtJar.getName() + " has no value for Specification-Version"); } return result; } catch (java.io.IOException e) { Assertions.UNREACHABLE("error getting rt.jar manifest!"); return null; } } public boolean isJava17Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.7"); } public boolean isJava16Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.6"); } public boolean isJava15Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.5"); } public boolean isJava14Libraries() throws IllegalStateException { return getJavaLibraryVersion().startsWith("1.4"); } /** * Creates a "serializable" version of the analysis scope. * * @return a "serializable" version of the analysis scope. * @throws NotSerializableException */ public ShallowAnalysisScope toShallowAnalysisScope() throws NotSerializableException { if (getArrayClassLoader().getNumberOfClasses() != 0) { throw new NotSerializableException("Scope was already used for building array classes"); } // Note: 'arrayClassLoader' object will be built from scratch in remote process // represent modules map as a set of strings (corresponding to analysis scope file lines. List moduleLines = new ArrayList(); for (Map.Entry> e : moduleMap.entrySet()) { ClassLoaderReference lrReference = e.getKey(); String moduleLdr = lrReference.getName().toString(); String moduleLang = lrReference.getLanguage().toString(); assert Language.JAVA.getName().equals(lrReference.getLanguage()) : "Java language only is currently supported"; for (Module m : e.getValue()) { String moduleType; String modulePath; if (m instanceof JarFileModule) { moduleType = "jarFile"; modulePath = ((JarFileModule) m).getAbsolutePath(); } else if (m instanceof BinaryDirectoryTreeModule) { moduleType = "binaryDir"; modulePath = ((BinaryDirectoryTreeModule) m).getPath(); } else if (m instanceof SourceDirectoryTreeModule) { moduleType = "sourceDir"; modulePath = ((SourceDirectoryTreeModule) m).getPath(); } else if (m instanceof SourceFileModule) { moduleType = "sourceFile"; modulePath = ((SourceFileModule) m).getAbsolutePath(); } else { Assertions.UNREACHABLE("Module type isn't supported - " + m); continue; } modulePath.replace("\\", "/"); String moduleDescrLine = String.format("%s,%s,%s,%s", moduleLdr, moduleLang, moduleType, modulePath); moduleLines.add(moduleDescrLine); } } // represent loaderImplByRef map as set of strings List ldrImplLines = new ArrayList(); for (Map.Entry e : loaderImplByRef.entrySet()) { ClassLoaderReference lrReference = e.getKey(); String ldrName = lrReference.getName().toString(); String ldrLang = lrReference.getLanguage().toString(); assert Language.JAVA.getName().equals(lrReference.getLanguage()) : "Java language only is currently supported"; String ldrImplName = e.getValue(); String ldrImplDescrLine = String.format("%s,%s,%s,%s", ldrName, ldrLang, "loaderImpl", ldrImplName); ldrImplLines.add(ldrImplDescrLine); } ShallowAnalysisScope shallowScope = new ShallowAnalysisScope(getExclusions(), moduleLines, ldrImplLines); return shallowScope; } }
File
AnalysisScope.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * A synthetic method from the {@link FakeRootClass}
 */
public abstract class AbstractRootMethod extends SyntheticMethod {

    statements.add(s);
  final protected ArrayList statements = new ArrayList();

  private Map constant2ValueNumber = HashMapFactory.make();

  /**
   * The number of the next local value number available for the fake root method. Note that we reserve value number 1 to represent
   * the value "any exception caught by the root method"
   */
  protected int nextLocal = 2;

  protected final IClassHierarchy cha;

  private final AnalysisOptions options;

  protected final AnalysisCache cache;

  protected final SSAInstructionFactory insts;

  public AbstractRootMethod(MethodReference method, IClass declaringClass, final IClassHierarchy cha, AnalysisOptions options,
      AnalysisCache cache) {
    super(method, declaringClass, true, false);
    this.cha = cha;
    this.options = options;
    this.cache = cache;
    this.insts = declaringClass.getClassLoader().getInstructionFactory();
    if (cache == null) {
      throw new IllegalArgumentException("null cache");
    }
    // I'd like to enforce that declaringClass is a FakeRootClass ... but CASt would currently break.
    // so checking dynamically instead.
    if (declaringClass instanceof FakeRootClass) {
      ((FakeRootClass) declaringClass).addMethod(this);
    }
  }

  public AbstractRootMethod(MethodReference method, final IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
    this(method, new FakeRootClass(cha), cha, options, cache);
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getStatements(com.ibm.wala.util.warnings.WarningSet)
   */
  @Override
  public SSAInstruction[] getStatements(SSAOptions options) {
    SSAInstruction[] result = new SSAInstruction[statements.size()];
    int i = 0;
    for (Iterator it = statements.iterator(); it.hasNext();) {
      result[i++] = it.next();
    }

    return result;
  }

  @Override
  public IR makeIR(Context context, SSAOptions options) {
    SSAInstruction instrs[] = getStatements(options);
    Map constants = null;
    if (!constant2ValueNumber.isEmpty()) {
      constants = HashMapFactory.make(constant2ValueNumber.size());
      for (ConstantValue c : constant2ValueNumber.keySet()) {
        int vn = constant2ValueNumber.get(c);
        constants.put(vn, c);
      }
    }
    InducedCFG cfg = makeControlFlowGraph(instrs);
    return new SyntheticIR(this, Everywhere.EVERYWHERE, cfg, instrs, options, constants);
  }

  public int addLocal() {
    return nextLocal++;
  }

  /**
   * @return the invoke instructions added by this operation
   * @throws IllegalArgumentException if site is null
   */
  public SSAInvokeInstruction addInvocation(int[] params, CallSiteReference site) {
    if (site == null) {
      throw new IllegalArgumentException("site is null");
    }
    CallSiteReference newSite = CallSiteReference.make(statements.size(), site.getDeclaredTarget(), site.getInvocationCode());
    SSAInvokeInstruction s = null;
    if (newSite.getDeclaredTarget().getReturnType().equals(TypeReference.Void)) {
      s = insts.InvokeInstruction(statements.size(), params, nextLocal++, newSite);
    } else {
      s = insts.InvokeInstruction(statements.size(), nextLocal++, params, nextLocal++, newSite);
    }
    statements.add(s);
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return s;
  }

  /**
   * Add a return statement
   */
  public SSAReturnInstruction addReturn(int vn, boolean isPrimitive) {
    SSAReturnInstruction s = insts.ReturnInstruction(statements.size(), vn, isPrimitive);
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return s;
  }

  /**
   * Add a New statement of the given type
   * 
   * Side effect: adds call to default constructor of given type if one exists.
   * 
   * @return instruction added, or null
   * @throws IllegalArgumentException if T is null
   */
  public SSANewInstruction addAllocation(TypeReference T) {
    return addAllocation(T, true);
  }

  /**
   * Add a New statement of the given array type and length
   */
  public SSANewInstruction add1DArrayAllocation(TypeReference T, int length) {
    int instance = nextLocal++;
    NewSiteReference ref = NewSiteReference.make(statements.size(), T);
    assert T.isArrayType();
    assert T.getDimensionality() == 1;
    int[] sizes = new int[1];
    Arrays.fill(sizes, getValueNumberForIntConstant(length));
    SSANewInstruction result = insts.NewInstruction(statements.size(), instance, ref, sizes);
    statements.add(result);
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return result;
  }

  /**
   * Add a New statement of the given type
   */
  public SSANewInstruction addAllocationWithoutCtor(TypeReference T) {
    return addAllocation(T, false);
  }

  /**
   * Add a New statement of the given type
   * 
   * @return instruction added, or null
   * @throws IllegalArgumentException if T is null
   */
  private SSANewInstruction addAllocation(TypeReference T, boolean invokeCtor) {
    if (T == null) {
      throw new IllegalArgumentException("T is null");
    }
    int instance = nextLocal++;
    SSANewInstruction result = null;

    if (T.isReferenceType()) {
      NewSiteReference ref = NewSiteReference.make(statements.size(), T);
      if (T.isArrayType()) {
        int[] sizes = new int[T.getDimensionality()];
        Arrays.fill(sizes, getValueNumberForIntConstant(1));
        result = insts.NewInstruction(statements.size(), instance, ref, sizes);
      } else {

        result = insts.NewInstruction(statements.size(), instance, ref);
      }
      statements.add(result);

      IClass klass = cha.lookupClass(T);
      if (klass == null) {
        Warnings.add(AllocationFailure.create(T));
        return null;
      }

      if (klass.isArrayClass()) {
        int arrayRef = result.getDef();
        TypeReference e = klass.getReference().getArrayElementType();
        while (e != null && !e.isPrimitiveType()) {
          // allocate an instance for the array contents
          NewSiteReference n = NewSiteReference.make(statements.size(), e);
          int alloc = nextLocal++;
          SSANewInstruction ni = null;
          if (e.isArrayType()) {
            int[] sizes = new int[T.getDimensionality()];
            Arrays.fill(sizes, getValueNumberForIntConstant(1));
            ni = insts.NewInstruction(statements.size(), alloc, n, sizes);
          } else {
            ni = insts.NewInstruction(statements.size(), alloc, n);
          }
          statements.add(ni);

          // emit an astore
          SSAArrayStoreInstruction store = insts.ArrayStoreInstruction(statements.size(), arrayRef, getValueNumberForIntConstant(0), alloc, e);
          statements.add(store);

          e = e.isArrayType() ? e.getArrayElementType() : null;
          arrayRef = alloc;
        }
      }
      if (invokeCtor) {
        IMethod ctor = cha.resolveMethod(klass, MethodReference.initSelector);
        if (ctor != null) {
          addInvocation(new int[] { instance }, CallSiteReference.make(statements.size(), ctor.getReference(),
              IInvokeInstruction.Dispatch.SPECIAL));
        }
      }
    }
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return result;
  }

  protected int getValueNumberForIntConstant(int c) {
    ConstantValue v = new ConstantValue(c);
    Integer result = constant2ValueNumber.get(v);
    if (result == null) {
      result = nextLocal++;
      constant2ValueNumber.put(v, result);
    }
    return result;
  }

  protected int getValueNumberForByteConstant(byte c) {
    // treat it like an int constant for now.
    ConstantValue v = new ConstantValue(c);
    Integer result = constant2ValueNumber.get(v);
    if (result == null) {
      result = nextLocal++;
      constant2ValueNumber.put(v, result);
    }
    return result;
  }

  protected int getValueNumberForCharConstant(char c) {
    // treat it like an int constant for now.
    ConstantValue v = new ConstantValue(c);
    Integer result = constant2ValueNumber.get(v);
    if (result == null) {
      result = nextLocal++;
      constant2ValueNumber.put(v, result);
    }
    return result;
  }

  /**
   * A warning for when we fail to allocate a type in the fake root method
   */
  private static class AllocationFailure extends Warning {

    final TypeReference t;

    AllocationFailure(TypeReference t) {
      super(Warning.SEVERE);
      this.t = t;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + t;
    }

    public static AllocationFailure create(TypeReference t) {
      return new AllocationFailure(t);
    }
  }

  public int addPhi(int[] values) {
    int result = nextLocal++;
    SSAPhiInstruction phi = insts.PhiInstruction(statements.size(), result, values);
    statements.add(phi);
    return result;
  }

  public int addGetInstance(FieldReference ref, int object) {
    int result = nextLocal++;
    statements.add(insts.GetInstruction(statements.size(), result, object, ref));
    return result;
  }

  public int addGetStatic(FieldReference ref) {
    int result = nextLocal++;
    statements.add(insts.GetInstruction(statements.size(), result, ref));
    return result;
  }

  public int addCheckcast(TypeReference[] types, int rv, boolean isPEI) {
    int lv = nextLocal++;

    statements.add(insts.CheckCastInstruction(statements.size(), lv, rv, types, isPEI));
    return lv;
  }

/** BEGIN Custom change: advanced synthetic instructions */
  public void addSetInstance(final FieldReference ref, final int baseObject, final int value) {
    statements.add(insts.PutInstruction(statements.size(), baseObject, value, ref));
  }
  
  public void addSetStatic(final FieldReference ref, final int value) {
    statements.add(insts.PutInstruction(statements.size(), value, ref));
  }
  
  public void addSetArrayField(final TypeReference elementType, final int baseObject, final int indexValue, final int value) {
    statements.add(insts.ArrayStoreInstruction(statements.size(), baseObject, indexValue, value, elementType));
  }

  public int addGetArrayField(final TypeReference elementType, final int baseObject, final int indexValue) {
    int result = nextLocal++;
    statements.add(insts.ArrayLoadInstruction(statements.size(), result, baseObject, indexValue, elementType));
    return result;
  }
/** END Custom change: advanced synthetic instructions */
  public RTAContextInterpreter getInterpreter() {
    return new RTAContextInterpreter() {

      public Iterator iterateNewSites(CGNode node) {
        ArrayList result = new ArrayList();
        SSAInstruction[] statements = getStatements(options.getSSAOptions());
        for (int i = 0; i < statements.length; i++) {
          if (statements[i] instanceof SSANewInstruction) {
            SSANewInstruction s = (SSANewInstruction) statements[i];
            result.add(s.getNewSite());
          }
        }
        return result.iterator();
      }

      public Iterator getInvokeStatements() {
        ArrayList result = new ArrayList();
        SSAInstruction[] statements = getStatements(options.getSSAOptions());
        for (int i = 0; i < statements.length; i++) {
          if (statements[i] instanceof SSAInvokeInstruction) {
            result.add(statements[i]);
          }
        }
        return result.iterator();
      }

      public Iterator iterateCallSites(CGNode node) {
        final Iterator I = getInvokeStatements();
        return new Iterator() {
          public boolean hasNext() {
            return I.hasNext();
          }

          public CallSiteReference next() {
            SSAInvokeInstruction s = (SSAInvokeInstruction) I.next();
            return s.getCallSite();
          }

          public void remove() {
            Assertions.UNREACHABLE();
          }
        };
      }

      public boolean understands(CGNode node) {
        return node.getMethod().getDeclaringClass().getReference().equals(FakeRootClass.FAKE_ROOT_CLASS);
      }

      public boolean recordFactoryType(CGNode node, IClass klass) {
        // not a factory type
        return false;
      }
      public Iterator iterateFieldsRead(CGNode node) {
        return EmptyIterator.instance();
      }

      public Iterator iterateFieldsWritten(CGNode node) {
        return EmptyIterator.instance();
      }
    };
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * A synthetic method from the {@link FakeRootClass}
 */
public abstract class AbstractRootMethod extends SyntheticMethod {

  final protected ArrayList statements = new ArrayList();

  private Map constant2ValueNumber = HashMapFactory.make();

  /**
   * The number of the next local value number available for the fake root method. Note that we reserve value number 1 to represent
   * the value "any exception caught by the root method"
   */
  protected int nextLocal = 2;

  protected final IClassHierarchy cha;

  private final AnalysisOptions options;

  protected final AnalysisCache cache;

  protected final SSAInstructionFactory insts;

  public AbstractRootMethod(MethodReference method, IClass declaringClass, final IClassHierarchy cha, AnalysisOptions options,
      AnalysisCache cache) {
    super(method, declaringClass, true, false);
    this.cha = cha;
    this.options = options;
    this.cache = cache;
    this.insts = declaringClass.getClassLoader().getInstructionFactory();
    if (cache == null) {
      throw new IllegalArgumentException("null cache");
    }
    // I'd like to enforce that declaringClass is a FakeRootClass ... but CASt would currently break.
    // so checking dynamically instead.
    if (declaringClass instanceof FakeRootClass) {
      ((FakeRootClass) declaringClass).addMethod(this);
    }
  }

  public AbstractRootMethod(MethodReference method, final IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
    this(method, new FakeRootClass(cha), cha, options, cache);
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getStatements(com.ibm.wala.util.warnings.WarningSet)
   */
  @Override
  public SSAInstruction[] getStatements(SSAOptions options) {
    SSAInstruction[] result = new SSAInstruction[statements.size()];
    int i = 0;
    for (Iterator it = statements.iterator(); it.hasNext();) {
      result[i++] = it.next();
    }

    return result;
  }

  @Override
  public IR makeIR(Context context, SSAOptions options) {
    SSAInstruction instrs[] = getStatements(options);
    Map constants = null;
    if (!constant2ValueNumber.isEmpty()) {
      constants = HashMapFactory.make(constant2ValueNumber.size());
      for (ConstantValue c : constant2ValueNumber.keySet()) {
        int vn = constant2ValueNumber.get(c);
        constants.put(vn, c);
      }
    }
    InducedCFG cfg = makeControlFlowGraph(instrs);
    return new SyntheticIR(this, Everywhere.EVERYWHERE, cfg, instrs, options, constants);
  }

  public int addLocal() {
    return nextLocal++;
  }

  /**
   * @return the invoke instructions added by this operation
   * @throws IllegalArgumentException if site is null
   */
  public SSAInvokeInstruction addInvocation(int[] params, CallSiteReference site) {
    if (site == null) {
      throw new IllegalArgumentException("site is null");
    }
    CallSiteReference newSite = CallSiteReference.make(statements.size(), site.getDeclaredTarget(), site.getInvocationCode());
    SSAInvokeInstruction s = null;
    if (newSite.getDeclaredTarget().getReturnType().equals(TypeReference.Void)) {
      s = insts.InvokeInstruction(params, nextLocal++, newSite);
    } else {
      s = insts.InvokeInstruction(nextLocal++, params, nextLocal++, newSite);
    }
    statements.add(s);
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return s;
  }

  /**
   * Add a return statement
   */
  public SSAReturnInstruction addReturn(int vn, boolean isPrimitive) {
    SSAReturnInstruction s = insts.ReturnInstruction(vn, isPrimitive);
    statements.add(s);
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return s;
  }

  /**
   * Add a New statement of the given type
   * 
   * Side effect: adds call to default constructor of given type if one exists.
   * 
   * @return instruction added, or null
   * @throws IllegalArgumentException if T is null
   */
  public SSANewInstruction addAllocation(TypeReference T) {
    return addAllocation(T, true);
  }

  /**
   * Add a New statement of the given array type and length
   */
  public SSANewInstruction add1DArrayAllocation(TypeReference T, int length) {
    int instance = nextLocal++;
    NewSiteReference ref = NewSiteReference.make(statements.size(), T);
    assert T.isArrayType();
    assert ((ArrayClass)cha.lookupClass(T)).getDimensionality() == 1;
    int[] sizes = new int[1];
    Arrays.fill(sizes, getValueNumberForIntConstant(length));
    SSANewInstruction result = insts.NewInstruction(instance, ref, sizes);
    statements.add(result);
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return result;
  }

  /**
   * Add a New statement of the given type
   */
  public SSANewInstruction addAllocationWithoutCtor(TypeReference T) {
    return addAllocation(T, false);
  }

  /**
   * Add a New statement of the given type
   * 
   * @return instruction added, or null
   * @throws IllegalArgumentException if T is null
   */
  private SSANewInstruction addAllocation(TypeReference T, boolean invokeCtor) {
    if (T == null) {
      throw new IllegalArgumentException("T is null");
    }
    int instance = nextLocal++;
    SSANewInstruction result = null;

    if (T.isReferenceType()) {
      NewSiteReference ref = NewSiteReference.make(statements.size(), T);
      if (T.isArrayType()) {
        int[] sizes = new int[((ArrayClass)cha.lookupClass(T)).getDimensionality()];
        Arrays.fill(sizes, getValueNumberForIntConstant(1));
        result = insts.NewInstruction(instance, ref, sizes);
      } else {
        result = insts.NewInstruction(instance, ref);
      }
      statements.add(result);

      IClass klass = cha.lookupClass(T);
      if (klass == null) {
        Warnings.add(AllocationFailure.create(T));
        return null;
      }

      if (klass.isArrayClass()) {
        int arrayRef = result.getDef();
        TypeReference e = klass.getReference().getArrayElementType();
        while (e != null && !e.isPrimitiveType()) {
          // allocate an instance for the array contents
          NewSiteReference n = NewSiteReference.make(statements.size(), e);
          int alloc = nextLocal++;
          SSANewInstruction ni = null;
          if (e.isArrayType()) {
            int[] sizes = new int[((ArrayClass)cha.lookupClass(T)).getDimensionality()];
            Arrays.fill(sizes, getValueNumberForIntConstant(1));
            ni = insts.NewInstruction(alloc, n, sizes);
          } else {
            ni = insts.NewInstruction(alloc, n);
          }
          statements.add(ni);

          // emit an astore
          SSAArrayStoreInstruction store = insts.ArrayStoreInstruction(arrayRef, getValueNumberForIntConstant(0), alloc, e);
          statements.add(store);

          e = e.isArrayType() ? e.getArrayElementType() : null;
          arrayRef = alloc;
        }
      }
      if (invokeCtor) {
        IMethod ctor = cha.resolveMethod(klass, MethodReference.initSelector);
        if (ctor != null) {
          addInvocation(new int[] { instance }, CallSiteReference.make(statements.size(), ctor.getReference(),
              IInvokeInstruction.Dispatch.SPECIAL));
        }
      }
    }
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return result;
  }

  protected int getValueNumberForIntConstant(int c) {
    ConstantValue v = new ConstantValue(c);
    Integer result = constant2ValueNumber.get(v);
    if (result == null) {
      result = nextLocal++;
      constant2ValueNumber.put(v, result);
    }
    return result;
  }

  protected int getValueNumberForByteConstant(byte c) {
    // treat it like an int constant for now.
    ConstantValue v = new ConstantValue(c);
    Integer result = constant2ValueNumber.get(v);
    if (result == null) {
      result = nextLocal++;
      constant2ValueNumber.put(v, result);
    }
    return result;
  }

  protected int getValueNumberForCharConstant(char c) {
    // treat it like an int constant for now.
    ConstantValue v = new ConstantValue(c);
    Integer result = constant2ValueNumber.get(v);
    if (result == null) {
      result = nextLocal++;
      constant2ValueNumber.put(v, result);
    }
    return result;
  }

  /**
   * A warning for when we fail to allocate a type in the fake root method
   */
  private static class AllocationFailure extends Warning {

    final TypeReference t;

    AllocationFailure(TypeReference t) {
      super(Warning.SEVERE);
      this.t = t;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + t;
    }

    public static AllocationFailure create(TypeReference t) {
      return new AllocationFailure(t);
    }
  }

  public int addPhi(int[] values) {
    int result = nextLocal++;
    SSAPhiInstruction phi = insts.PhiInstruction(result, values);
    statements.add(phi);
    return result;
  }

  public int addGetInstance(FieldReference ref, int object) {
    int result = nextLocal++;
    statements.add(insts.GetInstruction(result, object, ref));
    return result;
  }

  public int addGetStatic(FieldReference ref) {
    int result = nextLocal++;
    statements.add(insts.GetInstruction(result, ref));
    return result;
  }

  public int addCheckcast(TypeReference[] types, int rv, boolean isPEI) {
    int lv = nextLocal++;

    statements.add(insts.CheckCastInstruction(lv, rv, types, isPEI));
    return lv;
  }

  public RTAContextInterpreter getInterpreter() {
    return new RTAContextInterpreter() {

      public Iterator iterateNewSites(CGNode node) {
        ArrayList result = new ArrayList();
        SSAInstruction[] statements = getStatements(options.getSSAOptions());
        for (int i = 0; i < statements.length; i++) {
          if (statements[i] instanceof SSANewInstruction) {
            SSANewInstruction s = (SSANewInstruction) statements[i];
            result.add(s.getNewSite());
          }
        }
        return result.iterator();
      }

      public Iterator getInvokeStatements() {
        ArrayList result = new ArrayList();
        SSAInstruction[] statements = getStatements(options.getSSAOptions());
        for (int i = 0; i < statements.length; i++) {
          if (statements[i] instanceof SSAInvokeInstruction) {
            result.add(statements[i]);
          }
        }
        return result.iterator();
      }

      public Iterator iterateCallSites(CGNode node) {
        final Iterator I = getInvokeStatements();
        return new Iterator() {
          public boolean hasNext() {
            return I.hasNext();
          }

          public CallSiteReference next() {
            SSAInvokeInstruction s = (SSAInvokeInstruction) I.next();
            return s.getCallSite();
          }

          public void remove() {
            Assertions.UNREACHABLE();
          }
        };
      }

      public boolean understands(CGNode node) {
        return node.getMethod().getDeclaringClass().getReference().equals(FakeRootClass.FAKE_ROOT_CLASS);
      }

      public boolean recordFactoryType(CGNode node, IClass klass) {
        // not a factory type
        return false;
      }

      public Iterator iterateFieldsRead(CGNode node) {
        return EmptyIterator.instance();
      }

      public Iterator iterateFieldsWritten(CGNode node) {
        return EmptyIterator.instance();
      }
    };
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
  }
      throw new IllegalArgumentException("null cache");
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.SyntheticIR;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * A synthetic method from the {@link FakeRootClass}
 */
public abstract class AbstractRootMethod extends SyntheticMethod {

  final protected ArrayList statements = new ArrayList();

  private Map constant2ValueNumber = HashMapFactory.make();

  /**
   * The number of the next local value number available for the fake root method. Note that we reserve value number 1 to represent
   * the value "any exception caught by the root method"
   */
  protected int nextLocal = 2;

  protected final IClassHierarchy cha;

  private final AnalysisOptions options;
  protected final AnalysisCache cache;

  protected final SSAInstructionFactory insts;

  public AbstractRootMethod(MethodReference method, IClass declaringClass, final IClassHierarchy cha, AnalysisOptions options,
      AnalysisCache cache) {
    super(method, declaringClass, true, false);
    this.cha = cha;
    this.options = options;
    this.cache = cache;
    this.insts = declaringClass.getClassLoader().getInstructionFactory();
    if (cache == null) {
    }
    // I'd like to enforce that declaringClass is a FakeRootClass ... but CASt would currently break.
    // so checking dynamically instead.
    if (declaringClass instanceof FakeRootClass) {
      ((FakeRootClass) declaringClass).addMethod(this);
    }
  }

  public AbstractRootMethod(MethodReference method, final IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache) {
    this(method, new FakeRootClass(cha), cha, options, cache);
  }

  /*
   * @see com.ibm.wala.classLoader.IMethod#getStatements(com.ibm.wala.util.warnings.WarningSet)
   */
  @Override
  public SSAInstruction[] getStatements(SSAOptions options) {
    SSAInstruction[] result = new SSAInstruction[statements.size()];
    int i = 0;
    for (Iterator it = statements.iterator(); it.hasNext();) {
      result[i++] = it.next();
    }

    return result;
  }

  @Override
  public IR makeIR(Context context, SSAOptions options) {
    SSAInstruction instrs[] = getStatements(options);
    Map constants = null;
    if (!constant2ValueNumber.isEmpty()) {
      constants = HashMapFactory.make(constant2ValueNumber.size());
      for (ConstantValue c : constant2ValueNumber.keySet()) {
        int vn = constant2ValueNumber.get(c);
        constants.put(vn, c);
      }
    }
    InducedCFG cfg = makeControlFlowGraph(instrs);
    return new SyntheticIR(this, Everywhere.EVERYWHERE, cfg, instrs, options, constants);
  }

  public int addLocal() {
    return nextLocal++;
  }

  /**
   * @return the invoke instructions added by this operation
   * @throws IllegalArgumentException if site is null
   */
  public SSAInvokeInstruction addInvocation(int[] params, CallSiteReference site) {
    if (site == null) {
      throw new IllegalArgumentException("site is null");
    }
    CallSiteReference newSite = CallSiteReference.make(statements.size(), site.getDeclaredTarget(), site.getInvocationCode());
    SSAInvokeInstruction s = null;
    if (newSite.getDeclaredTarget().getReturnType().equals(TypeReference.Void)) {
      s = insts.InvokeInstruction(statements.size(), params, nextLocal++, newSite);
    } else {
      s = insts.InvokeInstruction(statements.size(), nextLocal++, params, nextLocal++, newSite);
    }
    statements.add(s);
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return s;
  }

  /**
   * Add a return statement
   */
  public SSAReturnInstruction addReturn(int vn, boolean isPrimitive) {
    SSAReturnInstruction s = insts.ReturnInstruction(statements.size(), vn, isPrimitive);
    statements.add(s);
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return s;
  }

  /**
   * Add a New statement of the given type
   * 
   * Side effect: adds call to default constructor of given type if one exists.
   * 
   * @return instruction added, or null
   * @throws IllegalArgumentException if T is null
   */
  public SSANewInstruction addAllocation(TypeReference T) {
    return addAllocation(T, true);

  /**
   * Add a New statement of the given array type and length
   */
  public SSANewInstruction add1DArrayAllocation(TypeReference T, int length) {
    int instance = nextLocal++;
    NewSiteReference ref = NewSiteReference.make(statements.size(), T);
    assert T.isArrayType();
    assert ((ArrayClass)cha.lookupClass(T)).getDimensionality() == 1;
    int[] sizes = new int[1];
    Arrays.fill(sizes, getValueNumberForIntConstant(length));
    SSANewInstruction result = insts.NewInstruction(statements.size(), instance, ref, sizes);
    statements.add(result);
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return result;
  }

  /**
   * Add a New statement of the given type
   */
  public SSANewInstruction addAllocationWithoutCtor(TypeReference T) {
    return addAllocation(T, false);
  }

  /**
   * Add a New statement of the given type
   * 
   * @return instruction added, or null
   * @throws IllegalArgumentException if T is null
   */
  private SSANewInstruction addAllocation(TypeReference T, boolean invokeCtor) {
    if (T == null) {
      throw new IllegalArgumentException("T is null");
    }
    int instance = nextLocal++;
    SSANewInstruction result = null;

    if (T.isReferenceType()) {
      NewSiteReference ref = NewSiteReference.make(statements.size(), T);
      if (T.isArrayType()) {
        int[] sizes = new int[((ArrayClass)cha.lookupClass(T)).getDimensionality()];
        Arrays.fill(sizes, getValueNumberForIntConstant(1));
        result = insts.NewInstruction(statements.size(), instance, ref, sizes);
      } else {
        result = insts.NewInstruction(statements.size(), instance, ref);
      }
      statements.add(result);

      IClass klass = cha.lookupClass(T);
      if (klass == null) {
        Warnings.add(AllocationFailure.create(T));
        return null;
      }

      if (klass.isArrayClass()) {
        int arrayRef = result.getDef();
        TypeReference e = klass.getReference().getArrayElementType();
        while (e != null && !e.isPrimitiveType()) {
          // allocate an instance for the array contents
          NewSiteReference n = NewSiteReference.make(statements.size(), e);
          int alloc = nextLocal++;
          SSANewInstruction ni = null;
          if (e.isArrayType()) {
            int[] sizes = new int[((ArrayClass)cha.lookupClass(T)).getDimensionality()];
            Arrays.fill(sizes, getValueNumberForIntConstant(1));
            ni = insts.NewInstruction(statements.size(), alloc, n, sizes);
          } else {
            ni = insts.NewInstruction(statements.size(), alloc, n);
          }
          statements.add(ni);

          // emit an astore
          SSAArrayStoreInstruction store = insts.ArrayStoreInstruction(statements.size(), arrayRef, getValueNumberForIntConstant(0), alloc, e);
          statements.add(store);

          e = e.isArrayType() ? e.getArrayElementType() : null;
          arrayRef = alloc;
        }
      }
      if (invokeCtor) {
        IMethod ctor = cha.resolveMethod(klass, MethodReference.initSelector);
        if (ctor != null) {
          addInvocation(new int[] { instance }, CallSiteReference.make(statements.size(), ctor.getReference(),
              IInvokeInstruction.Dispatch.SPECIAL));
        }
      }
    }
    cache.invalidate(this, Everywhere.EVERYWHERE);
    return result;
  }

  protected int getValueNumberForIntConstant(int c) {
    ConstantValue v = new ConstantValue(c);
    Integer result = constant2ValueNumber.get(v);
    if (result == null) {
      result = nextLocal++;
      constant2ValueNumber.put(v, result);
    }
    return result;
  }

  protected int getValueNumberForByteConstant(byte c) {
    // treat it like an int constant for now.
    ConstantValue v = new ConstantValue(c);
    Integer result = constant2ValueNumber.get(v);
    if (result == null) {
      result = nextLocal++;
      constant2ValueNumber.put(v, result);
    }
    return result;
  }

  protected int getValueNumberForCharConstant(char c) {
    // treat it like an int constant for now.
    ConstantValue v = new ConstantValue(c);
    Integer result = constant2ValueNumber.get(v);
    if (result == null) {
      result = nextLocal++;
      constant2ValueNumber.put(v, result);
    }
    return result;
  }

  /**
   * A warning for when we fail to allocate a type in the fake root method
   */
  private static class AllocationFailure extends Warning {

    final TypeReference t;

    AllocationFailure(TypeReference t) {
      super(Warning.SEVERE);
      this.t = t;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + t;
    }

    public static AllocationFailure create(TypeReference t) {
      return new AllocationFailure(t);
    }
  }

  public int addPhi(int[] values) {
    int result = nextLocal++;
    SSAPhiInstruction phi = insts.PhiInstruction(statements.size(), result, values);
    statements.add(phi);
    return result;
  }

  public int addGetInstance(FieldReference ref, int object) {
    int result = nextLocal++;
    statements.add(insts.GetInstruction(statements.size(), result, object, ref));
    return result;
  }

  public int addGetStatic(FieldReference ref) {
    int result = nextLocal++;
    statements.add(insts.GetInstruction(statements.size(), result, ref));
    return result;
  }

  public int addCheckcast(TypeReference[] types, int rv, boolean isPEI) {
    int lv = nextLocal++;

    statements.add(insts.CheckCastInstruction(statements.size(), lv, rv, types, isPEI));
    return lv;
  }

/** BEGIN Custom change: advanced synthetic instructions */
  public void addSetInstance(final FieldReference ref, final int baseObject, final int value) {
    statements.add(insts.PutInstruction(statements.size(), baseObject, value, ref));
  }
  
  public void addSetStatic(final FieldReference ref, final int value) {
    statements.add(insts.PutInstruction(statements.size(), value, ref));
  }
  
  public void addSetArrayField(final TypeReference elementType, final int baseObject, final int indexValue, final int value) {
    statements.add(insts.ArrayStoreInstruction(statements.size(), baseObject, indexValue, value, elementType));
  }

  public int addGetArrayField(final TypeReference elementType, final int baseObject, final int indexValue) {
    int result = nextLocal++;
    statements.add(insts.ArrayLoadInstruction(statements.size(), result, baseObject, indexValue, elementType));
    return result;
  }
/** END Custom change: advanced synthetic instructions */
  public RTAContextInterpreter getInterpreter() {
    return new RTAContextInterpreter() {

      public Iterator iterateNewSites(CGNode node) {
        ArrayList result = new ArrayList();
        SSAInstruction[] statements = getStatements(options.getSSAOptions());
        for (int i = 0; i < statements.length; i++) {
          if (statements[i] instanceof SSANewInstruction) {
            SSANewInstruction s = (SSANewInstruction) statements[i];
            result.add(s.getNewSite());
          }
        }
        return result.iterator();
      }

      public Iterator getInvokeStatements() {
        ArrayList result = new ArrayList();
        SSAInstruction[] statements = getStatements(options.getSSAOptions());
        for (int i = 0; i < statements.length; i++) {
          if (statements[i] instanceof SSAInvokeInstruction) {
            result.add(statements[i]);
          }
        }
        return result.iterator();
      }

      public Iterator iterateCallSites(CGNode node) {
        final Iterator I = getInvokeStatements();
        return new Iterator() {
          public boolean hasNext() {
            return I.hasNext();
          }

          public CallSiteReference next() {
            SSAInvokeInstruction s = (SSAInvokeInstruction) I.next();
            return s.getCallSite();
          }

          public void remove() {
            Assertions.UNREACHABLE();
          }
        };
      }

      public boolean understands(CGNode node) {
        return node.getMethod().getDeclaringClass().getReference().equals(FakeRootClass.FAKE_ROOT_CLASS);
      }

      public boolean recordFactoryType(CGNode node, IClass klass) {
        // not a factory type
        return false;
      }

      public Iterator iterateFieldsRead(CGNode node) {
        return EmptyIterator.instance();
      }

      public Iterator iterateFieldsWritten(CGNode node) {
        return EmptyIterator.instance();
      }
    };
  }
}
File
AbstractRootMethod.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
    }
    }

      throw new IllegalArgumentException("options is null");
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.impl;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.CallGraphBuilder;
import com.ibm.wala.ipa.callgraph.ClassTargetSelector;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder;
import com.ibm.wala.ipa.callgraph.propagation.cfa.ZeroXCFABuilder;
import com.ibm.wala.ipa.callgraph.propagation.cfa.ZeroXContainerCFABuilder;
import com.ibm.wala.ipa.callgraph.propagation.cfa.ZeroXInstanceKeys;
import com.ibm.wala.ipa.callgraph.propagation.rta.BasicRTABuilder;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.BypassClassTargetSelector;
import com.ibm.wala.ipa.summaries.BypassMethodTargetSelector;
import com.ibm.wala.ipa.summaries.XMLMethodSummaryReader;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.strings.Atom;

/**
 * Call graph utilities
 */
public class Util {
  /**
   * TODO: Make these properties?
   */
  public static String nativeSpec = "natives.xml";

/** BEGIN Custom change: change native spec */
  public static void setNativeSpec(String xmlFile) {
    nativeSpec = xmlFile;
  }
  
  public static String getNativeSpec() {
    return nativeSpec;
  }
/** END Custom change: change native spec */  
  /**
   * Set up an AnalysisOptions object with default selectors, corresponding to class hierarchy lookup
   * 
   * @throws IllegalArgumentException if options is null
   */
  public static void addDefaultSelectors(AnalysisOptions options, IClassHierarchy cha) {
    if (options == null) {
    options.setSelector(new ClassHierarchyMethodTargetSelector(cha));
    options.setSelector(new ClassHierarchyClassTargetSelector(cha));
  }

  /**
   * Modify an options object to include bypass logic as specified by a an XML file.
   * 
   * @throws IllegalArgumentException if scope is null
   * @throws IllegalArgumentException if cl is null
   * @throws IllegalArgumentException if options is null
   * @throws IllegalArgumentException if scope is null
   */
  public static void addBypassLogic(AnalysisOptions options, AnalysisScope scope, ClassLoader cl, String xmlFile,
      IClassHierarchy cha) throws IllegalArgumentException {
    if (scope == null) {
      throw new IllegalArgumentException("scope is null");
    }
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    if (cl == null) {
      throw new IllegalArgumentException("cl is null");
    }
    if (cha == null) {
      throw new IllegalArgumentException("cha cannot be null");
    }

    InputStream s = cl.getResourceAsStream(xmlFile);
    XMLMethodSummaryReader summary = new XMLMethodSummaryReader(s, scope);

/** BEGIN Custom change: load xml from file system as a fallback */
    addBypassLogic(options, scope, cl, summary, cha);
/** END Custom change: load xml from file system as a fallback */
  }

/** BEGIN Custom change: load xml from file system as a fallback */
  public static void addBypassLogic(AnalysisOptions options, AnalysisScope scope, ClassLoader cl, XMLMethodSummaryReader summary,
      IClassHierarchy cha) throws IllegalArgumentException {
    if (scope == null) {
      throw new IllegalArgumentException("scope is null");
    }
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    if (cl == null) {
      throw new IllegalArgumentException("cl is null");
    }
    if (cha == null) {
      throw new IllegalArgumentException("cha cannot be null");
    }    
    
    MethodTargetSelector ms = new BypassMethodTargetSelector(options.getMethodTargetSelector(), summary.getSummaries(), 
        summary.getIgnoredPackages(), cha);
    options.setSelector(ms);

    ClassTargetSelector cs = new BypassClassTargetSelector(options.getClassTargetSelector(), summary.getAllocatableClasses(), cha,
        cha.getLoader(scope.getLoader(Atom.findOrCreateUnicodeAtom("Synthetic"))));
    options.setSelector(cs);
  }
  
/** END Custom change: load xml from file system as a fallback */
  /**
   * @param scope
   * @param cha
   * @return set of all eligible Main classes in the class hierarchy
   * @throws IllegalArgumentException if scope is null
   */
  public static Iterable makeMainEntrypoints(AnalysisScope scope, IClassHierarchy cha) {
    if (scope == null) {
      throw new IllegalArgumentException("scope is null");
    }
    return makeMainEntrypoints(scope.getApplicationLoader(), cha);
  }

  public static Iterable makeMainEntrypoints(ClassLoaderReference clr, IClassHierarchy cha) {
    if (cha == null) {
      throw new IllegalArgumentException("cha is null");
    }
    final Atom mainMethod = Atom.findOrCreateAsciiAtom("main");
    final HashSet result = HashSetFactory.make();
    for (IClass klass : cha) {
      if (klass.getClassLoader().getReference().equals(clr)) {
        MethodReference mainRef = MethodReference.findOrCreate(klass.getReference(), mainMethod, Descriptor
            .findOrCreateUTF8("([Ljava/lang/String;)V"));
        IMethod m = klass.getMethod(mainRef.getSelector());
        if (m != null) {
          result.add(new DefaultEntrypoint(m, cha));
        }
      }
    }
    return new Iterable() {
      public Iterator iterator() {
   */
        return result.iterator();
      }
    };
  }

  /**
   * @return Entrypoints object for a Main J2SE class
   */
  public static Iterable makeMainEntrypoints(AnalysisScope scope, final IClassHierarchy cha, String className) {
    return makeMainEntrypoints(scope, cha, new String[] { className });
  }

  public static Iterable makeMainEntrypoints(final AnalysisScope scope, final IClassHierarchy cha,
      final String[] classNames) {
    if (scope == null) {
      throw new IllegalArgumentException("scope is null");
    }
    return makeMainEntrypoints(scope.getApplicationLoader(), cha, classNames);
  }

  /**
   * @return Entrypoints for a set of J2SE Main classes
   * @throws IllegalArgumentException if classNames == null
   * @throws IllegalArgumentException if (classNames != null) and (0 < classNames.length) and (classNames[0] == null)
   * @throws IllegalArgumentException if classNames.length == 0
   */
  public static Iterable makeMainEntrypoints(final ClassLoaderReference loaderRef, final IClassHierarchy cha,
      final String[] classNames) throws IllegalArgumentException, IllegalArgumentException, IllegalArgumentException {

    if (classNames == null) {
      throw new IllegalArgumentException("classNames == null");
    }
    if (classNames.length == 0) {
      throw new IllegalArgumentException("classNames.length == 0");
    }
    if (classNames[0] == null && 0 < classNames.length) {
      throw new IllegalArgumentException("(0 < classNames.length) and (classNames[0] == null)");
    for (int i = 0; i < classNames.length; i++) {
      if (classNames[i].indexOf("L") != 0) {
        throw new IllegalArgumentException("Expected class name to start with L " + classNames[i]);
      }
      if (classNames[i].indexOf(".") > 0) {
        Assertions.productionAssertion(false, "Expected class name formatted with /, not . " + classNames[i]);
      }
    }

    return new Iterable() {
      public Iterator iterator() {
        final Atom mainMethod = Atom.findOrCreateAsciiAtom("main");
        return new Iterator() {
          private int index = 0;

          public void remove() {
            Assertions.UNREACHABLE();
          }

          public boolean hasNext() {
            return index < classNames.length;
          }

          public Entrypoint next() {
            TypeReference T = TypeReference.findOrCreate(loaderRef, TypeName.string2TypeName(classNames[index++]));
            MethodReference mainRef = MethodReference.findOrCreate(T, mainMethod, Descriptor
                .findOrCreateUTF8("([Ljava/lang/String;)V"));
            return new DefaultEntrypoint(mainRef, cha);
          }
        };
      }
    };
  }

  /**
   * create a set holding the contents of an {@link Iterator}
   */
  public static  Set setify(Iterator x) {
    if (x == null) {
      throw new IllegalArgumentException("Null x");
    }
    Set y = HashSetFactory.make();
    while (x.hasNext()) {
      y.add(x.next());
    }
    return y;
  }

  /**
   * @param supG
   * @param subG
   * @throws IllegalArgumentException if subG is null
   * @throws IllegalArgumentException if supG is null
   */
  public static  void checkGraphSubset(Graph supG, Graph subG) {
    if (supG == null) {
      throw new IllegalArgumentException("supG is null");
    }
    if (subG == null) {
      throw new IllegalArgumentException("subG is null");
    }
    Set nodeDiff = setify(subG.iterator());
    nodeDiff.removeAll(setify(supG.iterator()));
    if (!nodeDiff.isEmpty()) {
      System.err.println("supergraph: ");
      System.err.println(supG.toString());
      System.err.println("subgraph: ");
      System.err.println(subG.toString());
      System.err.println("nodeDiff: ");
      for (Iterator it = nodeDiff.iterator(); it.hasNext();) {
        System.err.println(it.next().toString());
      }
      Assertions.productionAssertion(nodeDiff.isEmpty(), "bad superset, see tracefile\n");
    }

    for (Iterator subNodes = subG.iterator(); subNodes.hasNext();) {
      T m = subNodes.next();

      Set succDiff = setify(subG.getSuccNodes(m));
      succDiff.removeAll(setify(supG.getSuccNodes(m)));
      if (!succDiff.isEmpty()) {
        Assertions.productionAssertion(succDiff.isEmpty(), "bad superset for successors of " + m + ":" + succDiff);
      }

      Set predDiff = setify(subG.getPredNodes(m));
      predDiff.removeAll(setify(supG.getPredNodes(m)));
      if (!predDiff.isEmpty()) {
        System.err.println("supergraph: ");
        System.err.println(supG.toString());
        System.err.println("subgraph: ");
        System.err.println(subG.toString());
        System.err.println("predDiff: ");
        for (Iterator it = predDiff.iterator(); it.hasNext();) {
          System.err.println(it.next().toString());
        }
        Assertions.UNREACHABLE("bad superset for predecessors of " + m + ":" + predDiff);
      }
    }
  }
  /**
   * @return an RTA Call Graph builder.
   * 
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   */
  public static CallGraphBuilder makeRTABuilder(AnalysisOptions options, AnalysisCache cache, IClassHierarchy cha,
      AnalysisScope scope) {

    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return new BasicRTABuilder(cha, options, cache, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-CFA Call Graph Builder.
   */
  public static SSAPropagationCallGraphBuilder makeZeroCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {
    return makeZeroCFABuilder(options, cache, cha, scope, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @param customSelector user-defined context selector, or null if none
   * @param customInterpreter user-defined context interpreter, or null if none
   * @return a 0-CFA Call Graph Builder.
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope, ContextSelector customSelector, SSAContextInterpreter customInterpreter) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return ZeroXCFABuilder.make(cha, options, cache, customSelector, customInterpreter, ZeroXInstanceKeys.NONE);
  }

  /**
   * @return a 0-1-CFA Call Graph Builder.
   * 
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
  public static SSAPropagationCallGraphBuilder makeZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {
    return makeZeroOneCFABuilder(options, cache, cha, scope, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @param customSelector user-defined context selector, or null if none
   * @param customInterpreter user-defined context interpreter, or null if none
   * @return a 0-1-CFA Call Graph Builder.
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeVanillaZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope, ContextSelector customSelector, SSAContextInterpreter customInterpreter) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return ZeroXCFABuilder.make(cha, options, cache, customSelector, customInterpreter, ZeroXInstanceKeys.ALLOCATIONS | ZeroXInstanceKeys.CONSTANT_SPECIFIC);
  }

  /**
   * @return a 0-1-CFA Call Graph Builder.
   * 
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   */
  public static SSAPropagationCallGraphBuilder makeVanillaZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
  /**
      IClassHierarchy cha, AnalysisScope scope) {
    return makeVanillaZeroOneCFABuilder(options, cache, cha, scope, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @param customSelector user-defined context selector, or null if none
   * @param customInterpreter user-defined context interpreter, or null if none
   * @return a 0-1-CFA Call Graph Builder.
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope, ContextSelector customSelector, SSAContextInterpreter customInterpreter) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return ZeroXCFABuilder.make(cha, options, cache, customSelector, customInterpreter, ZeroXInstanceKeys.ALLOCATIONS | ZeroXInstanceKeys.SMUSH_MANY | ZeroXInstanceKeys.SMUSH_PRIMITIVE_HOLDERS
        | ZeroXInstanceKeys.SMUSH_STRINGS | ZeroXInstanceKeys.SMUSH_THROWABLES);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-CFA Call Graph Builder augmented with extra logic for containers
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroContainerCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);
    ContextSelector appSelector = null;
    SSAContextInterpreter appInterpreter = null;

    return new ZeroXContainerCFABuilder(cha, options, cache, appSelector, appInterpreter, ZeroXInstanceKeys.NONE);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-1-CFA Call Graph Builder augmented with extra logic for containers
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroOneContainerCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);
    ContextSelector appSelector = null;
    SSAContextInterpreter appInterpreter = null;

    return new ZeroXContainerCFABuilder(cha, options, cache, appSelector, appInterpreter, ZeroXInstanceKeys.ALLOCATIONS | ZeroXInstanceKeys.SMUSH_MANY | ZeroXInstanceKeys.SMUSH_PRIMITIVE_HOLDERS
        | ZeroXInstanceKeys.SMUSH_STRINGS | ZeroXInstanceKeys.SMUSH_THROWABLES);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-1-CFA Call Graph Builder augmented with extra logic for containers
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeVanillaZeroOneContainerCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);
    ContextSelector appSelector = null;
    SSAContextInterpreter appInterpreter = null;
    options.setUseConstantSpecificKeys(true);

    return new ZeroXContainerCFABuilder(cha, options, cache, appSelector, appInterpreter, ZeroXInstanceKeys.ALLOCATIONS);

  }

  public static void addDefaultBypassLogic(AnalysisOptions options, AnalysisScope scope, ClassLoader cl, IClassHierarchy cha) {
/** BEGIN Custom change: load xml from file system as a fallback */
    if (cl.getResourceAsStream(nativeSpec) != null) {
/** END Custom change: load xml from file system as fallback */
      addBypassLogic(options, scope, cl, nativeSpec, cha);
/** BEGIN Custom change: load xml from file system as a fallback */
    } else {
      // try to load from filesystem
      try {
        BufferedInputStream bIn = new BufferedInputStream(new FileInputStream(nativeSpec));
        XMLMethodSummaryReader reader = new XMLMethodSummaryReader(bIn, scope);
        addBypassLogic(options, scope, cl, reader, cha);
      } catch (FileNotFoundException e) {
        System.err.println("Could not load natives xml file from: " + nativeSpec);
        e.printStackTrace();
      }
    }
/** END Custom change: load xml from file system as fallback */
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.impl;

import java.io.InputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.CallGraphBuilder;
import com.ibm.wala.ipa.callgraph.ClassTargetSelector;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder;
import com.ibm.wala.ipa.callgraph.propagation.cfa.ZeroXCFABuilder;
import com.ibm.wala.ipa.callgraph.propagation.cfa.ZeroXContainerCFABuilder;
import com.ibm.wala.ipa.callgraph.propagation.cfa.ZeroXInstanceKeys;
import com.ibm.wala.ipa.callgraph.propagation.rta.BasicRTABuilder;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.BypassClassTargetSelector;
import com.ibm.wala.ipa.summaries.BypassMethodTargetSelector;
import com.ibm.wala.ipa.summaries.XMLMethodSummaryReader;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.strings.Atom;

/**
 * Call graph utilities
 */
public class Util {
  /**
   * TODO: Make these properties?
   */
  public static final String nativeSpec = "natives.xml";

  /**
   * Set up an AnalysisOptions object with default selectors, corresponding to class hierarchy lookup
   * 
   * @throws IllegalArgumentException if options is null
   */
   * @return Entrypoints for a set of J2SE Main classes
  public static void addDefaultSelectors(AnalysisOptions options, IClassHierarchy cha) {
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    options.setSelector(new ClassHierarchyMethodTargetSelector(cha));
    options.setSelector(new ClassHierarchyClassTargetSelector(cha));
  }

  /**
   * Modify an options object to include bypass logic as specified by a an XML file.
   * 
   * @throws IllegalArgumentException if scope is null
   * @throws IllegalArgumentException if cl is null
   * @throws IllegalArgumentException if options is null
   * @throws IllegalArgumentException if scope is null
   */
  public static void addBypassLogic(AnalysisOptions options, AnalysisScope scope, ClassLoader cl, String xmlFile,
      IClassHierarchy cha) throws IllegalArgumentException {
    if (scope == null) {
      throw new IllegalArgumentException("scope is null");
    }
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    if (cl == null) {
      throw new IllegalArgumentException("cl is null");
    }
    if (cha == null) {
      throw new IllegalArgumentException("cha cannot be null");
    }

    InputStream s = cl.getResourceAsStream(xmlFile);
    XMLMethodSummaryReader summary = new XMLMethodSummaryReader(s, scope);

    MethodTargetSelector ms = new BypassMethodTargetSelector(options.getMethodTargetSelector(), summary.getSummaries(), summary
        .getIgnoredPackages(), cha);
    options.setSelector(ms);

    ClassTargetSelector cs = new BypassClassTargetSelector(options.getClassTargetSelector(), summary.getAllocatableClasses(), cha,
        cha.getLoader(scope.getLoader(Atom.findOrCreateUnicodeAtom("Synthetic"))));
    options.setSelector(cs);
  }

  /**
   * @param scope
   * @param cha
   * @return set of all eligible Main classes in the class hierarchy
   * @throws IllegalArgumentException if scope is null
   */
  public static Iterable makeMainEntrypoints(AnalysisScope scope, IClassHierarchy cha) {
    if (scope == null) {
      throw new IllegalArgumentException("scope is null");
    }
    return makeMainEntrypoints(scope.getApplicationLoader(), cha);
  }

  public static Iterable makeMainEntrypoints(ClassLoaderReference clr, IClassHierarchy cha) {
    if (cha == null) {
      throw new IllegalArgumentException("cha is null");
    }
    final Atom mainMethod = Atom.findOrCreateAsciiAtom("main");
    final HashSet result = HashSetFactory.make();
    for (IClass klass : cha) {
      if (klass.getClassLoader().getReference().equals(clr)) {
        MethodReference mainRef = MethodReference.findOrCreate(klass.getReference(), mainMethod, Descriptor
            .findOrCreateUTF8("([Ljava/lang/String;)V"));
        IMethod m = klass.getMethod(mainRef.getSelector());
        if (m != null) {
          result.add(new DefaultEntrypoint(m, cha));
        }
      }
    }
    return new Iterable() {
      public Iterator iterator() {
        return result.iterator();
      }
    };
  }

  /**
   * @return Entrypoints object for a Main J2SE class
   */
  public static Iterable makeMainEntrypoints(AnalysisScope scope, final IClassHierarchy cha, String className) {
    return makeMainEntrypoints(scope, cha, new String[] { className });
  }

  public static Iterable makeMainEntrypoints(final AnalysisScope scope, final IClassHierarchy cha,
      final String[] classNames) {
    if (scope == null) {
      throw new IllegalArgumentException("scope is null");
    }
    return makeMainEntrypoints(scope.getApplicationLoader(), cha, classNames);
  }

   * @throws IllegalArgumentException if classNames == null
   * @throws IllegalArgumentException if (classNames != null) and (0 < classNames.length) and (classNames[0] == null)
   * @throws IllegalArgumentException if classNames.length == 0
   */
  public static Iterable makeMainEntrypoints(final ClassLoaderReference loaderRef, final IClassHierarchy cha,
      final String[] classNames) throws IllegalArgumentException, IllegalArgumentException, IllegalArgumentException {

    if (classNames == null) {
      throw new IllegalArgumentException("classNames == null");
    }
    if (classNames.length == 0) {
      throw new IllegalArgumentException("classNames.length == 0");
    }
    if (classNames[0] == null && 0 < classNames.length) {
      throw new IllegalArgumentException("(0 < classNames.length) and (classNames[0] == null)");
    }

    for (int i = 0; i < classNames.length; i++) {
      if (classNames[i].indexOf("L") != 0) {
        throw new IllegalArgumentException("Expected class name to start with L " + classNames[i]);
      }
      if (classNames[i].indexOf(".") > 0) {
        Assertions.productionAssertion(false, "Expected class name formatted with /, not . " + classNames[i]);
      }
    }

    return new Iterable() {
      public Iterator iterator() {
        final Atom mainMethod = Atom.findOrCreateAsciiAtom("main");
        return new Iterator() {
          private int index = 0;

          public void remove() {
            Assertions.UNREACHABLE();
          }

          public boolean hasNext() {
            return index < classNames.length;
          }

          public Entrypoint next() {
            TypeReference T = TypeReference.findOrCreate(loaderRef, TypeName.string2TypeName(classNames[index++]));
            MethodReference mainRef = MethodReference.findOrCreate(T, mainMethod, Descriptor
                .findOrCreateUTF8("([Ljava/lang/String;)V"));
            return new DefaultEntrypoint(mainRef, cha);
          }
        };
      }
    };
  }

  /**
   * create a set holding the contents of an {@link Iterator}
   */
  public static  Set setify(Iterator x) {
    if (x == null) {
      throw new IllegalArgumentException("Null x");
    }
    Set y = HashSetFactory.make();
    while (x.hasNext()) {
      y.add(x.next());
    }
    return y;
  }

  /**
   * @param supG
   * @param subG
   * @throws IllegalArgumentException if subG is null
   * @throws IllegalArgumentException if supG is null
   */
  public static  void checkGraphSubset(Graph supG, Graph subG) {
    if (supG == null) {
      throw new IllegalArgumentException("supG is null");
    }
    if (subG == null) {
      throw new IllegalArgumentException("subG is null");
    }
    Set nodeDiff = setify(subG.iterator());
    nodeDiff.removeAll(setify(supG.iterator()));
    if (!nodeDiff.isEmpty()) {
      System.err.println("supergraph: ");
      System.err.println(supG.toString());
      System.err.println("subgraph: ");
      System.err.println(subG.toString());
      System.err.println("nodeDiff: ");
      for (Iterator it = nodeDiff.iterator(); it.hasNext();) {
        System.err.println(it.next().toString());
      }
      Assertions.productionAssertion(nodeDiff.isEmpty(), "bad superset, see tracefile\n");
    }

    for (Iterator subNodes = subG.iterator(); subNodes.hasNext();) {
      T m = subNodes.next();

      Set succDiff = setify(subG.getSuccNodes(m));
      succDiff.removeAll(setify(supG.getSuccNodes(m)));
      if (!succDiff.isEmpty()) {
        Assertions.productionAssertion(succDiff.isEmpty(), "bad superset for successors of " + m + ":" + succDiff);
      }

      Set predDiff = setify(subG.getPredNodes(m));
      predDiff.removeAll(setify(supG.getPredNodes(m)));
      if (!predDiff.isEmpty()) {
        System.err.println("supergraph: ");
        System.err.println(supG.toString());
        System.err.println("subgraph: ");
        System.err.println(subG.toString());
        System.err.println("predDiff: ");
        for (Iterator it = predDiff.iterator(); it.hasNext();) {
          System.err.println(it.next().toString());
        }
        Assertions.UNREACHABLE("bad superset for predecessors of " + m + ":" + predDiff);
      }
    }
  }

  /**
   * @return an RTA Call Graph builder.
   * 
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   */
  public static CallGraphBuilder makeRTABuilder(AnalysisOptions options, AnalysisCache cache, IClassHierarchy cha,
      AnalysisScope scope) {

    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return new BasicRTABuilder(cha, options, cache, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-CFA Call Graph Builder.
   */
  public static SSAPropagationCallGraphBuilder makeZeroCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {
    return makeZeroCFABuilder(options, cache, cha, scope, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @param customSelector user-defined context selector, or null if none
   * @param customInterpreter user-defined context interpreter, or null if none
   * @return a 0-CFA Call Graph Builder.
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope, ContextSelector customSelector, SSAContextInterpreter customInterpreter) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return ZeroXCFABuilder.make(cha, options, cache, customSelector, customInterpreter, ZeroXInstanceKeys.NONE);
  }

  /**
   * @return a 0-1-CFA Call Graph Builder.
   * 
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   */
  public static SSAPropagationCallGraphBuilder makeZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {
    return makeZeroOneCFABuilder(options, cache, cha, scope, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @param customSelector user-defined context selector, or null if none
   * @param customInterpreter user-defined context interpreter, or null if none
   * @return a 0-1-CFA Call Graph Builder.
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeVanillaZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope, ContextSelector customSelector, SSAContextInterpreter customInterpreter) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return ZeroXCFABuilder.make(cha, options, cache, customSelector, customInterpreter, ZeroXInstanceKeys.ALLOCATIONS | ZeroXInstanceKeys.CONSTANT_SPECIFIC);
  }

  /**
   * @return a 0-1-CFA Call Graph Builder.
   * 
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   */
  public static SSAPropagationCallGraphBuilder makeVanillaZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {
    return makeVanillaZeroOneCFABuilder(options, cache, cha, scope, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @param customSelector user-defined context selector, or null if none
   * @param customInterpreter user-defined context interpreter, or null if none
   * @return a 0-1-CFA Call Graph Builder.
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope, ContextSelector customSelector, SSAContextInterpreter customInterpreter) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return ZeroXCFABuilder.make(cha, options, cache, customSelector, customInterpreter, ZeroXInstanceKeys.ALLOCATIONS | ZeroXInstanceKeys.SMUSH_MANY | ZeroXInstanceKeys.SMUSH_PRIMITIVE_HOLDERS
        | ZeroXInstanceKeys.SMUSH_STRINGS | ZeroXInstanceKeys.SMUSH_THROWABLES);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-CFA Call Graph Builder augmented with extra logic for containers
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroContainerCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);
    ContextSelector appSelector = null;
    SSAContextInterpreter appInterpreter = null;

    return new ZeroXContainerCFABuilder(cha, options, cache, appSelector, appInterpreter, ZeroXInstanceKeys.NONE);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-1-CFA Call Graph Builder augmented with extra logic for containers
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroOneContainerCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);
    ContextSelector appSelector = null;
    SSAContextInterpreter appInterpreter = null;

    return new ZeroXContainerCFABuilder(cha, options, cache, appSelector, appInterpreter, ZeroXInstanceKeys.ALLOCATIONS | ZeroXInstanceKeys.SMUSH_MANY | ZeroXInstanceKeys.SMUSH_PRIMITIVE_HOLDERS
        | ZeroXInstanceKeys.SMUSH_STRINGS | ZeroXInstanceKeys.SMUSH_THROWABLES);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-1-CFA Call Graph Builder augmented with extra logic for containers
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeVanillaZeroOneContainerCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);
    ContextSelector appSelector = null;
    SSAContextInterpreter appInterpreter = null;
    options.setUseConstantSpecificKeys(true);

    return new ZeroXContainerCFABuilder(cha, options, cache, appSelector, appInterpreter, ZeroXInstanceKeys.ALLOCATIONS);

  }

  public static void addDefaultBypassLogic(AnalysisOptions options, AnalysisScope scope, ClassLoader cl, IClassHierarchy cha) {
    addBypassLogic(options, scope, cl, nativeSpec, cha);
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.impl;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.CallGraphBuilder;
import com.ibm.wala.ipa.callgraph.ClassTargetSelector;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter;
import com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder;
import com.ibm.wala.ipa.callgraph.propagation.cfa.ZeroXCFABuilder;
import com.ibm.wala.ipa.callgraph.propagation.cfa.ZeroXContainerCFABuilder;
import com.ibm.wala.ipa.callgraph.propagation.cfa.ZeroXInstanceKeys;
import com.ibm.wala.ipa.callgraph.propagation.rta.BasicRTABuilder;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ipa.summaries.BypassClassTargetSelector;
import com.ibm.wala.ipa.summaries.BypassMethodTargetSelector;
import com.ibm.wala.ipa.summaries.XMLMethodSummaryReader;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.strings.Atom;

/**
 * Call graph utilities
 */
public class Util {
  /**
   * TODO: Make these properties?
   */
  public static String nativeSpec = "natives.xml";

/** BEGIN Custom change: change native spec */
  public static void setNativeSpec(String xmlFile) {
    nativeSpec = xmlFile;
  }
  
  public static String getNativeSpec() {
    return nativeSpec;
  }
/** END Custom change: change native spec */  
  /**
   * Set up an AnalysisOptions object with default selectors, corresponding to class hierarchy lookup
   * 
   * @throws IllegalArgumentException if options is null
   */
  public static void addDefaultSelectors(AnalysisOptions options, IClassHierarchy cha) {
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    options.setSelector(new ClassHierarchyMethodTargetSelector(cha));
    options.setSelector(new ClassHierarchyClassTargetSelector(cha));
  }

  /**
   * Modify an options object to include bypass logic as specified by a an XML file.
   * 
   * @throws IllegalArgumentException if scope is null
   * @throws IllegalArgumentException if cl is null
   * @throws IllegalArgumentException if options is null
   * @throws IllegalArgumentException if scope is null
   */
  public static void addBypassLogic(AnalysisOptions options, AnalysisScope scope, ClassLoader cl, String xmlFile,
      IClassHierarchy cha) throws IllegalArgumentException {
    if (scope == null) {
      throw new IllegalArgumentException("scope is null");
    }
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    if (cl == null) {
      throw new IllegalArgumentException("cl is null");
    }
    if (cha == null) {
      throw new IllegalArgumentException("cha cannot be null");
    }

    InputStream s = cl.getResourceAsStream(xmlFile);
    XMLMethodSummaryReader summary = new XMLMethodSummaryReader(s, scope);

/** BEGIN Custom change: load xml from file system as a fallback */
    addBypassLogic(options, scope, cl, summary, cha);
/** END Custom change: load xml from file system as a fallback */
  }

/** BEGIN Custom change: load xml from file system as a fallback */
  public static void addBypassLogic(AnalysisOptions options, AnalysisScope scope, ClassLoader cl, XMLMethodSummaryReader summary,
      IClassHierarchy cha) throws IllegalArgumentException {
    if (scope == null) {
      throw new IllegalArgumentException("scope is null");
    }
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    if (cl == null) {
      throw new IllegalArgumentException("cl is null");
    }
    if (cha == null) {
      throw new IllegalArgumentException("cha cannot be null");
    }    
    
    MethodTargetSelector ms = new BypassMethodTargetSelector(options.getMethodTargetSelector(), summary.getSummaries(), 
        summary.getIgnoredPackages(), cha);
    options.setSelector(ms);

    ClassTargetSelector cs = new BypassClassTargetSelector(options.getClassTargetSelector(), summary.getAllocatableClasses(), cha,
          public boolean hasNext() {
        cha.getLoader(scope.getLoader(Atom.findOrCreateUnicodeAtom("Synthetic"))));
    options.setSelector(cs);
  }
  
/** END Custom change: load xml from file system as a fallback */
  /**
   * @param scope
   * @param cha
   * @return set of all eligible Main classes in the class hierarchy
   * @throws IllegalArgumentException if scope is null
   */
  public static Iterable makeMainEntrypoints(AnalysisScope scope, IClassHierarchy cha) {
    if (scope == null) {
      throw new IllegalArgumentException("scope is null");
    }
    return makeMainEntrypoints(scope.getApplicationLoader(), cha);
  }

  public static Iterable makeMainEntrypoints(ClassLoaderReference clr, IClassHierarchy cha) {
    if (cha == null) {
      throw new IllegalArgumentException("cha is null");
    }
    final Atom mainMethod = Atom.findOrCreateAsciiAtom("main");
    final HashSet result = HashSetFactory.make();
    for (IClass klass : cha) {
      if (klass.getClassLoader().getReference().equals(clr)) {
        MethodReference mainRef = MethodReference.findOrCreate(klass.getReference(), mainMethod, Descriptor
            .findOrCreateUTF8("([Ljava/lang/String;)V"));
        IMethod m = klass.getMethod(mainRef.getSelector());
        if (m != null) {
          result.add(new DefaultEntrypoint(m, cha));
        }
      }
    }
    return new Iterable() {
      public Iterator iterator() {
        return result.iterator();
      }
    };
  }

  /**
   * @return Entrypoints object for a Main J2SE class
   */
  public static Iterable makeMainEntrypoints(AnalysisScope scope, final IClassHierarchy cha, String className) {
    return makeMainEntrypoints(scope, cha, new String[] { className });
  }

  public static Iterable makeMainEntrypoints(final AnalysisScope scope, final IClassHierarchy cha,
      final String[] classNames) {
    if (scope == null) {
      throw new IllegalArgumentException("scope is null");
    }
    return makeMainEntrypoints(scope.getApplicationLoader(), cha, classNames);
  }

  /**
   * @return Entrypoints for a set of J2SE Main classes
   * @throws IllegalArgumentException if classNames == null
   * @throws IllegalArgumentException if (classNames != null) and (0 < classNames.length) and (classNames[0] == null)
   * @throws IllegalArgumentException if classNames.length == 0
   */
  public static Iterable makeMainEntrypoints(final ClassLoaderReference loaderRef, final IClassHierarchy cha,
      final String[] classNames) throws IllegalArgumentException, IllegalArgumentException, IllegalArgumentException {

    if (classNames == null) {
      throw new IllegalArgumentException("classNames == null");
    }
    if (classNames.length == 0) {
      throw new IllegalArgumentException("classNames.length == 0");
    }
    if (classNames[0] == null && 0 < classNames.length) {
      throw new IllegalArgumentException("(0 < classNames.length) and (classNames[0] == null)");
    }

    for (int i = 0; i < classNames.length; i++) {
      if (classNames[i].indexOf("L") != 0) {
        throw new IllegalArgumentException("Expected class name to start with L " + classNames[i]);
      }
      if (classNames[i].indexOf(".") > 0) {
        Assertions.productionAssertion(false, "Expected class name formatted with /, not . " + classNames[i]);
      }
    }

    return new Iterable() {
      public Iterator iterator() {
        final Atom mainMethod = Atom.findOrCreateAsciiAtom("main");
        return new Iterator() {
          private int index = 0;

          public void remove() {
            Assertions.UNREACHABLE();
          }

            return index < classNames.length;
          }

          public Entrypoint next() {
            TypeReference T = TypeReference.findOrCreate(loaderRef, TypeName.string2TypeName(classNames[index++]));
            MethodReference mainRef = MethodReference.findOrCreate(T, mainMethod, Descriptor
                .findOrCreateUTF8("([Ljava/lang/String;)V"));
            return new DefaultEntrypoint(mainRef, cha);
          }
        };
      }
    };
  }

  /**
   * create a set holding the contents of an {@link Iterator}
   */
  public static  Set setify(Iterator x) {
    if (x == null) {
      throw new IllegalArgumentException("Null x");
    }
    Set y = HashSetFactory.make();
    while (x.hasNext()) {
      y.add(x.next());
    }
    return y;
  }

  /**
   * @param supG
   * @param subG
   * @throws IllegalArgumentException if subG is null
   * @throws IllegalArgumentException if supG is null
   */
  public static  void checkGraphSubset(Graph supG, Graph subG) {
    if (supG == null) {
      throw new IllegalArgumentException("supG is null");
    }
    if (subG == null) {
      throw new IllegalArgumentException("subG is null");
    }
    Set nodeDiff = setify(subG.iterator());
    nodeDiff.removeAll(setify(supG.iterator()));
    if (!nodeDiff.isEmpty()) {
      System.err.println("supergraph: ");
      System.err.println(supG.toString());
      System.err.println("subgraph: ");
      System.err.println(subG.toString());
      System.err.println("nodeDiff: ");
      for (Iterator it = nodeDiff.iterator(); it.hasNext();) {
        System.err.println(it.next().toString());
      }
      Assertions.productionAssertion(nodeDiff.isEmpty(), "bad superset, see tracefile\n");
    }

    for (Iterator subNodes = subG.iterator(); subNodes.hasNext();) {
      T m = subNodes.next();

      Set succDiff = setify(subG.getSuccNodes(m));
      succDiff.removeAll(setify(supG.getSuccNodes(m)));
      if (!succDiff.isEmpty()) {
        Assertions.productionAssertion(succDiff.isEmpty(), "bad superset for successors of " + m + ":" + succDiff);
      }

      Set predDiff = setify(subG.getPredNodes(m));
      predDiff.removeAll(setify(supG.getPredNodes(m)));
      if (!predDiff.isEmpty()) {
        System.err.println("supergraph: ");
        System.err.println(supG.toString());
        System.err.println("subgraph: ");
        System.err.println(subG.toString());
        System.err.println("predDiff: ");
        for (Iterator it = predDiff.iterator(); it.hasNext();) {
          System.err.println(it.next().toString());
        }
        Assertions.UNREACHABLE("bad superset for predecessors of " + m + ":" + predDiff);
      }
    }
  }

  /**
   * @return an RTA Call Graph builder.
   * 
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   */
  public static CallGraphBuilder makeRTABuilder(AnalysisOptions options, AnalysisCache cache, IClassHierarchy cha,
      AnalysisScope scope) {

    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return new BasicRTABuilder(cha, options, cache, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-CFA Call Graph Builder.
   */
  public static SSAPropagationCallGraphBuilder makeZeroCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {
    return makeZeroCFABuilder(options, cache, cha, scope, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @param customSelector user-defined context selector, or null if none
   * @param customInterpreter user-defined context interpreter, or null if none
   * @return a 0-CFA Call Graph Builder.
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope, ContextSelector customSelector, SSAContextInterpreter customInterpreter) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return ZeroXCFABuilder.make(cha, options, cache, customSelector, customInterpreter, ZeroXInstanceKeys.NONE);
  }

  /**
   * @return a 0-1-CFA Call Graph Builder.
   * 
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   */
  public static SSAPropagationCallGraphBuilder makeZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {
    return makeZeroOneCFABuilder(options, cache, cha, scope, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @param customSelector user-defined context selector, or null if none
   * @param customInterpreter user-defined context interpreter, or null if none
   * @return a 0-1-CFA Call Graph Builder.
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeVanillaZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope, ContextSelector customSelector, SSAContextInterpreter customInterpreter) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return ZeroXCFABuilder.make(cha, options, cache, customSelector, customInterpreter, ZeroXInstanceKeys.ALLOCATIONS | ZeroXInstanceKeys.CONSTANT_SPECIFIC);
  }

  /**
   * @return a 0-1-CFA Call Graph Builder.
   * 
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   */
  public static SSAPropagationCallGraphBuilder makeVanillaZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {
    return makeVanillaZeroOneCFABuilder(options, cache, cha, scope, null, null);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @param customSelector user-defined context selector, or null if none
   * @param customInterpreter user-defined context interpreter, or null if none
   * @return a 0-1-CFA Call Graph Builder.
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroOneCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope, ContextSelector customSelector, SSAContextInterpreter customInterpreter) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);

    return ZeroXCFABuilder.make(cha, options, cache, customSelector, customInterpreter, ZeroXInstanceKeys.ALLOCATIONS | ZeroXInstanceKeys.SMUSH_MANY | ZeroXInstanceKeys.SMUSH_PRIMITIVE_HOLDERS
        | ZeroXInstanceKeys.SMUSH_STRINGS | ZeroXInstanceKeys.SMUSH_THROWABLES);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-CFA Call Graph Builder augmented with extra logic for containers
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroContainerCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);
    ContextSelector appSelector = null;
    SSAContextInterpreter appInterpreter = null;

    return new ZeroXContainerCFABuilder(cha, options, cache, appSelector, appInterpreter, ZeroXInstanceKeys.NONE);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-1-CFA Call Graph Builder augmented with extra logic for containers
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeZeroOneContainerCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);
    ContextSelector appSelector = null;
    SSAContextInterpreter appInterpreter = null;

    return new ZeroXContainerCFABuilder(cha, options, cache, appSelector, appInterpreter, ZeroXInstanceKeys.ALLOCATIONS | ZeroXInstanceKeys.SMUSH_MANY | ZeroXInstanceKeys.SMUSH_PRIMITIVE_HOLDERS
        | ZeroXInstanceKeys.SMUSH_STRINGS | ZeroXInstanceKeys.SMUSH_THROWABLES);
  }

  /**
   * @param options options that govern call graph construction
   * @param cha governing class hierarchy
   * @param scope representation of the analysis scope
   * @return a 0-1-CFA Call Graph Builder augmented with extra logic for containers
   * @throws IllegalArgumentException if options is null
   */
  public static SSAPropagationCallGraphBuilder makeVanillaZeroOneContainerCFABuilder(AnalysisOptions options, AnalysisCache cache,
      IClassHierarchy cha, AnalysisScope scope) {

    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    addDefaultSelectors(options, cha);
    addDefaultBypassLogic(options, scope, Util.class.getClassLoader(), cha);
    ContextSelector appSelector = null;
    SSAContextInterpreter appInterpreter = null;
    options.setUseConstantSpecificKeys(true);

    return new ZeroXContainerCFABuilder(cha, options, cache, appSelector, appInterpreter, ZeroXInstanceKeys.ALLOCATIONS);

  }

  public static void addDefaultBypassLogic(AnalysisOptions options, AnalysisScope scope, ClassLoader cl, IClassHierarchy cha) {
/** BEGIN Custom change: load xml from file system as a fallback */
    if (cl.getResourceAsStream(nativeSpec) != null) {
/** END Custom change: load xml from file system as fallback */
      addBypassLogic(options, scope, cl, nativeSpec, cha);
/** BEGIN Custom change: load xml from file system as a fallback */
    } else {
      // try to load from filesystem
      try {
        BufferedInputStream bIn = new BufferedInputStream(new FileInputStream(nativeSpec));
        XMLMethodSummaryReader reader = new XMLMethodSummaryReader(bIn, scope);
        addBypassLogic(options, scope, cl, reader, cha);
      } catch (FileNotFoundException e) {
        System.err.println("Could not load natives xml file from: " + nativeSpec);
        e.printStackTrace();
      }
    }
/** END Custom change: load xml from file system as fallback */
  }

}
File
Util.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
  }
  /**
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.propagation;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.ibm.wala.analysis.reflection.IllegalArgumentExceptionContext;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticClass;
import com.ibm.wala.fixpoint.UnaryOperator;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.CallGraphBuilder;
import com.ibm.wala.ipa.callgraph.CallGraphBuilderCancelException;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.CancelRuntimeException;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetAction;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * This abstract base class provides the general algorithm for a call graph builder that relies on propagation through an iterative
 * dataflow solver
 * 
 * TODO: This implementation currently keeps all points to sets live ... even those for local variables that do not span
 * interprocedural boundaries. This may be too space-inefficient .. we can consider recomputing local sets on demand.
 */
public abstract class PropagationCallGraphBuilder implements CallGraphBuilder {
  private final static boolean DEBUG_ALL = false;

  final static boolean DEBUG_ASSIGN = DEBUG_ALL | false;

  private final static boolean DEBUG_ARRAY_LOAD = DEBUG_ALL | false;

  private final static boolean DEBUG_ARRAY_STORE = DEBUG_ALL | false;

  private final static boolean DEBUG_FILTER = DEBUG_ALL | false;

  final protected static boolean DEBUG_GENERAL = DEBUG_ALL | false;

  private final static boolean DEBUG_GET = DEBUG_ALL | false;

  private final static boolean DEBUG_PUT = DEBUG_ALL | false;

  private final static boolean DEBUG_ENTRYPOINTS = DEBUG_ALL | false;

  /**
   * Meta-data regarding how pointers are modeled
   */
  protected PointerKeyFactory pointerKeyFactory;

  /**
   * The object that represents the java.lang.Object class
   */
  final private IClass JAVA_LANG_OBJECT;

  /**
   * Governing class hierarchy
   */
  final protected IClassHierarchy cha;

  /**
   * Special rules for bypassing Java calls
   */
  final protected AnalysisOptions options;

  /**
   * Cache of IRs and things
   */
  private final AnalysisCache analysisCache;

  /**
   * Set of nodes that have already been traversed for constraints
   */
  final private Set alreadyVisited = HashSetFactory.make();

  /**
   * At any given time, the set of nodes that have been discovered but not yet processed for constraints
   */
  private Set discoveredNodes = HashSetFactory.make();

  /**
   * Set of calls (CallSiteReferences) that are created by entrypoints
   */
  final protected Set entrypointCallSites = HashSetFactory.make();

  /**
   * The system of constraints used to build this graph
   */
  protected PropagationSystem system;

  /**
   * Algorithm used to solve the system of constraints
   */
  private IPointsToSolver solver;

  /**
   * The call graph under construction
   */
  protected final ExplicitCallGraph callGraph;

  /**
   * Singleton operator for assignments
   */
  protected final static AssignOperator assignOperator = new AssignOperator();

  /**
   * singleton operator for filter
   */
  public final FilterOperator filterOperator = new FilterOperator();

  /**
   * singleton operator for inverse filter
   */
  protected final InverseFilterOperator inverseFilterOperator = new InverseFilterOperator();

  /**
   * An object which interprets methods in context
   */
  private SSAContextInterpreter contextInterpreter;

  /**
   * A context selector which may use information derived from the propagation-based dataflow.
   */
  protected ContextSelector contextSelector;

  /**
   * An object that abstracts how to model instances in the heap.
   */
  protected InstanceKeyFactory instanceKeyFactory;

   * Algorithmic choice: should the GetfieldOperator and PutfieldOperator cache its previous history to reduce work?
   */
  final private boolean rememberGetPutHistory = true;

  /**
   * @param cha governing class hierarchy
   * @param options governing call graph construction options
   * @param pointerKeyFactory factory which embodies pointer abstraction policy
   */
  protected PropagationCallGraphBuilder(IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache,
      PointerKeyFactory pointerKeyFactory) {
    if (cha == null) {
      throw new IllegalArgumentException("cha is null");
    }
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    assert cache != null;
    this.cha = cha;
    this.options = options;
    this.analysisCache = cache;
    // we need pointer keys to handle reflection
    assert pointerKeyFactory != null;
    this.pointerKeyFactory = pointerKeyFactory;
    callGraph = createEmptyCallGraph(cha, options);
    try {
      callGraph.init();
    } catch (CancelException e) {
      if (DEBUG_GENERAL) {
        System.err.println("Could not initialize the call graph due to node number constraints: " + e.getMessage());
      }
    }
    callGraph.setInterpreter(contextInterpreter);
    JAVA_LANG_OBJECT = cha.lookupClass(TypeReference.JavaLangObject);
  }

  protected ExplicitCallGraph createEmptyCallGraph(IClassHierarchy cha, AnalysisOptions options) {
    return new ExplicitCallGraph(cha, options, getAnalysisCache());
  }

  /**
   * @return true iff the klass represents java.lang.Object
   */
  protected boolean isJavaLangObject(IClass klass) {
    return (klass.getReference().equals(TypeReference.JavaLangObject));
  }

  public CallGraph makeCallGraph(AnalysisOptions options) throws IllegalArgumentException, CancelException {
    return makeCallGraph(options, null);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.CallGraphBuilder#makeCallGraph(com.ibm.wala.ipa.callgraph.AnalysisOptions)
   */
  public CallGraph makeCallGraph(AnalysisOptions options, IProgressMonitor monitor) throws IllegalArgumentException,
      CallGraphBuilderCancelException {
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    system = makeSystem(options);

    if (DEBUG_GENERAL) {
      System.err.println("Enter makeCallGraph!");
    }

    if (DEBUG_GENERAL) {
      System.err.println("Initialized call graph");
    }

    system.setMinEquationsForTopSort(options.getMinEquationsForTopSort());
    system.setTopologicalGrowthFactor(options.getTopologicalGrowthFactor());
    system.setMaxEvalBetweenTopo(options.getMaxEvalBetweenTopo());

    discoveredNodes = HashSetFactory.make();
    discoveredNodes.add(callGraph.getFakeRootNode());

    // Set up the initially reachable methods and classes
    for (Iterator it = options.getEntrypoints().iterator(); it.hasNext();) {
      Entrypoint E = (Entrypoint) it.next();
      if (DEBUG_ENTRYPOINTS) {
        System.err.println("Entrypoint: " + E);
      }
      SSAAbstractInvokeInstruction call = E.addCall((AbstractRootMethod) callGraph.getFakeRootNode().getMethod());

      if (call == null) {
        Warnings.add(EntrypointResolutionWarning.create(E));
      } else {
        entrypointCallSites.add(call.getCallSite());
      }
    }
    
/** BEGIN Custom change: throw exception on empty entry points. This is a severe issue that should not go undetected! */
    if (entrypointCallSites.isEmpty()) {
      throw new IllegalStateException("Could not create a entrypoint callsites."
  public IClass getJavaLangObject() {
          + " This happens when some parameters of the method can not be generated automatically "
          + "(e.g. when they refer to an interface or an abstract class).");
    }
    
/** END Custom change: throw exception on empty entry points. This is a severe issue that should not go undetected! */
    customInit();

    solver = makeSolver();
    try {
      solver.solve(monitor);
    } catch (CancelException e) {
      CallGraphBuilderCancelException c = CallGraphBuilderCancelException.createCallGraphBuilderCancelException(e, callGraph,
          system.extractPointerAnalysis(this));
      throw c;
    } catch (CancelRuntimeException e) {
      CallGraphBuilderCancelException c = CallGraphBuilderCancelException.createCallGraphBuilderCancelException(e, callGraph,
          system.extractPointerAnalysis(this));
      throw c;
    }

    return callGraph;
  }

  protected PropagationSystem makeSystem(AnalysisOptions options) {
    return new PropagationSystem(callGraph, pointerKeyFactory, instanceKeyFactory);
  }

  protected abstract IPointsToSolver makeSolver();

  /**
   * A warning for when we fail to resolve a call to an entrypoint
   */
  private static class EntrypointResolutionWarning extends Warning {

    final Entrypoint entrypoint;

    EntrypointResolutionWarning(Entrypoint entrypoint) {
      super(Warning.SEVERE);
      this.entrypoint = entrypoint;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + entrypoint;
    }

    public static EntrypointResolutionWarning create(Entrypoint entrypoint) {
      return new EntrypointResolutionWarning(entrypoint);
    }
  }

  protected void customInit() {
  }

  /**
   * Add constraints for a node.
   * @param monitor 
   * 
   * @return true iff any new constraints are added.
   */

  /**
  protected abstract boolean addConstraintsFromNode(CGNode n, IProgressMonitor monitor) throws CancelException;

  /**
   * Add constraints from newly discovered nodes. Note: the act of adding constraints may discover new nodes, so this routine is
   * iterative.
   * 
   * @return true iff any new constraints are added.
   * @throws CancelException 
   */
  protected boolean addConstraintsFromNewNodes(IProgressMonitor monitor) throws CancelException {
    boolean result = false;
    while (!discoveredNodes.isEmpty()) {
      Iterator it = discoveredNodes.iterator();
      discoveredNodes = HashSetFactory.make();
      while (it.hasNext()) {
        CGNode n = it.next();
        result |= addConstraintsFromNode(n, monitor);
      }
    }
    return result;
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the local variable identified by
   *         the value number parameter.
   */
  public PointerKey getPointerKeyForLocal(CGNode node, int valueNumber) {
    return pointerKeyFactory.getPointerKeyForLocal(node, valueNumber);
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the local variable identified by
   *         the value number parameter.
   */
  public FilteredPointerKey getFilteredPointerKeyForLocal(CGNode node, int valueNumber, FilteredPointerKey.TypeFilter filter) {
    assert filter != null;
    return pointerKeyFactory.getFilteredPointerKeyForLocal(node, valueNumber, filter);
  }


  public FilteredPointerKey getFilteredPointerKeyForLocal(CGNode node, int valueNumber, IClass filter) {
    return getFilteredPointerKeyForLocal(node, valueNumber, new FilteredPointerKey.SingleClassFilter(filter));
  }

  public FilteredPointerKey getFilteredPointerKeyForLocal(CGNode node, int valueNumber, InstanceKey filter) {
    return getFilteredPointerKeyForLocal(node, valueNumber, new FilteredPointerKey.SingleInstanceFilter(filter));
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the return value for a node
   */
  public PointerKey getPointerKeyForReturnValue(CGNode node) {
    return pointerKeyFactory.getPointerKeyForReturnValue(node);
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the exceptional return value
   */
  public PointerKey getPointerKeyForExceptionalReturnValue(CGNode node) {
    return pointerKeyFactory.getPointerKeyForExceptionalReturnValue(node);
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the contents of the static field
   */
  public PointerKey getPointerKeyForStaticField(IField f) {
    assert f != null : "null FieldReference";
    return pointerKeyFactory.getPointerKeyForStaticField(f);
  }

  /**
   * @return the PointerKey that acts as a representation for the class of pointers that includes the given instance field. null if
   *         there's some problem.
   * @throws IllegalArgumentException if I is null
   * @throws IllegalArgumentException if field is null
   */
  public PointerKey getPointerKeyForInstanceField(InstanceKey I, IField field) {
    if (field == null) {
      throw new IllegalArgumentException("field is null");
    }
    if (I == null) {
      throw new IllegalArgumentException("I is null");
    }
    IClass t = field.getDeclaringClass();
    IClass C = I.getConcreteType();
    if (!(C instanceof SyntheticClass)) {
      if (!getClassHierarchy().isSubclassOf(C, t)) {
        return null;
      }
    }

    return pointerKeyFactory.getPointerKeyForInstanceField(I, field);
  }
   * TODO: expand this API to differentiate between different array indices
   * 
   * @param I an InstanceKey representing an abstract array
   * @return the PointerKey that acts as a representation for the class of pointers that includes the given array contents, or null
   *         if none found.
   * @throws IllegalArgumentException if I is null
   */
  public PointerKey getPointerKeyForArrayContents(InstanceKey I) {
    if (I == null) {
      throw new IllegalArgumentException("I is null");
    }
    IClass C = I.getConcreteType();
    if (!C.isArrayClass()) {
      assert false : "illegal arguments: " + I;
    }
    return pointerKeyFactory.getPointerKeyForArrayContents(I);
  }

  /**
   * Handle assign of a particular exception instance into an exception variable
   * 
   * @param exceptionVar points-to set for a variable representing a caught exception
   * @param catchClasses set of TypeReferences that the exceptionVar may catch
   * @param e a particular exception instance
   */
  protected void assignInstanceToCatch(PointerKey exceptionVar, Set catchClasses, InstanceKey e) {
    if (catches(catchClasses, e.getConcreteType(), cha)) {
      system.newConstraint(exceptionVar, e);
    }
  }

  /**
   * Generate a set of constraints to represent assignment to an exception variable in a catch clause. Note that we use
   * FilterOperator to filter out types that the exception handler doesn't catch.
   * 
   * @param exceptionVar points-to set for a variable representing a caught exception
   * @param catchClasses set of TypeReferences that the exceptionVar may catch
   * @param e points-to-set representing a thrown exception that might be caught.
   */
  protected void addAssignmentsForCatchPointerKey(PointerKey exceptionVar, Set catchClasses, PointerKey e) {
    if (DEBUG_GENERAL) {
      System.err.println("addAssignmentsForCatch: " + catchClasses);
    }
    // this is tricky ... we want to filter based on a number of classes ... so we can't
    // just used a FilteredPointerKey for the exceptionVar. Instead, we create a new
    // "typed local" for each catch class, and coalesce the results using
    // assignment
    for (IClass c : catchClasses) {
      if (c.getReference().equals(c.getClassLoader().getLanguage().getThrowableType())) {
        system.newConstraint(exceptionVar, assignOperator, e);
      } else {
        FilteredPointerKey typedException = TypedPointerKey.make(exceptionVar, c);
        system.newConstraint(typedException, filterOperator, e);
        system.newConstraint(exceptionVar, assignOperator, typedException);
      }
    }
  }

  /**
   * A warning for when we fail to resolve a call to an entrypoint
   */
  @SuppressWarnings("unused")
  private static class ExceptionLookupFailure extends Warning {

    final TypeReference t;

    ExceptionLookupFailure(TypeReference t) {
      super(Warning.SEVERE);
      this.t = t;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + t;
    }

    public static ExceptionLookupFailure create(TypeReference t) {
      return new ExceptionLookupFailure(t);
    }
  }

  /**
   * A pointer key that delegates to an untyped variant, but adds a type filter
   */
  public final static class TypedPointerKey implements FilteredPointerKey {

    private final IClass type;

    private final PointerKey base;

    static TypedPointerKey make(PointerKey base, IClass type) {
      assert type != null;
      return new TypedPointerKey(base, type);
    }

    private TypedPointerKey(PointerKey base, IClass type) {
      this.type = type;
      this.base = base;
      assert type != null;
      assert !(type instanceof FilteredPointerKey);
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey#getTypeFilter()
     */
    public TypeFilter getTypeFilter() {
      return new SingleClassFilter(type);
    }

    @Override
    public boolean equals(Object obj) {
      // instanceof is OK because this class is final
      if (obj instanceof TypedPointerKey) {
        TypedPointerKey other = (TypedPointerKey) obj;
        return type.equals(other.type) && base.equals(other.base);
      } else {
        return false;
      }
    }

    @Override
    public int hashCode() {
      return 67931 * base.hashCode() + type.hashCode();
    }

    @Override
    public String toString() {
      return "{ " + base + " type: " + type + "}";
    }

    public PointerKey getBase() {
      return base;
    }
  }

  /**
   * @param catchClasses Set of TypeReference
   * @param klass an Exception Class
   * @return true iff klass is a subclass of some element of the Set
   * @throws IllegalArgumentException if catchClasses is null
   */
    return JAVA_LANG_OBJECT;
  public static boolean catches(Set catchClasses, IClass klass, IClassHierarchy cha) {
    if (catchClasses == null) {
      throw new IllegalArgumentException("catchClasses is null");
    }
    // quick shortcut
    if (catchClasses.size() == 1) {
      IClass c = catchClasses.iterator().next();
      if (c != null && c.getReference().equals(TypeReference.JavaLangThread)) {
        return true;
      }
    }
    for (IClass c : catchClasses) {
      if (c != null && cha.isAssignableFrom(c, klass)) {
        return true;
      }
    }
    return false;
  }

  public static boolean representsNullType(InstanceKey key) throws IllegalArgumentException {
    if (key == null) {
      throw new IllegalArgumentException("key == null");
    }
    IClass cls = key.getConcreteType();
    Language L = cls.getClassLoader().getLanguage();
    return L.isNullType(cls.getReference());
  }

  /**
   * The FilterOperator is a filtered set-union. i.e. the LHS is `unioned' with the RHS, but filtered by the set associated with
   * this operator instance. The filter is the set of InstanceKeys corresponding to the target type of this cast. This is still
   * monotonic.
   * 
   * LHS U= (RHS n k)
   * 
   * 
   * Unary op: := Cast_k( )
   * 
   * (Again, technically a binary op -- see note for Assign)
   * 
   * TODO: these need to be canonicalized.
   * 
   */
  public class FilterOperator extends UnaryOperator implements IPointerOperator {

    protected FilterOperator() {
    }

    /*
     * @see com.ibm.wala.dataflow.UnaryOperator#evaluate(com.ibm.wala.dataflow.IVariable, com.ibm.wala.dataflow.IVariable)
     */
    @Override
    public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable rhs) {

      FilteredPointerKey pk = (FilteredPointerKey) lhs.getPointerKey();

      if (DEBUG_FILTER) {
        String S = "EVAL Filter " + lhs.getPointerKey() + " " + rhs.getPointerKey();
        S += "\nEVAL      " + lhs + " " + rhs;
        System.err.println(S);
      }
      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }

      boolean changed = false;
      FilteredPointerKey.TypeFilter filter = pk.getTypeFilter();
      changed = filter.addFiltered(system, lhs, rhs);

      if (DEBUG_FILTER) {
        System.err.println("RESULT " + lhs + (changed ? " (changed)" : ""));
      }

      return changed ? CHANGED : NOT_CHANGED;
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return false;
    }

    @Override
    public String toString() {
      return "Filter ";
    }

    @Override
    public boolean equals(Object obj) {
      // these objects are canonicalized for the duration of a solve
      return this == obj;
    }

    @Override
    public int hashCode() {
      return 88651;
    }

  }

  public IClassHierarchy getClassHierarchy() {
    return cha;
  }

  public AnalysisOptions getOptions() {
    return options;
  }

  public ExplicitCallGraph getCallGraph() {
    return callGraph;
  }

  /**
   * Subclasses must register the context interpreter before building a call graph.
   */
  public void setContextInterpreter(SSAContextInterpreter interpreter) {
    contextInterpreter = interpreter;
    callGraph.setInterpreter(interpreter);
  }

  /*
   * @see com.ibm.detox.ipa.callgraph.CallGraphBuilder#getPointerAnalysis()
   */
  public PointerAnalysis getPointerAnalysis() {
    return system.extractPointerAnalysis(this);
  }

  public PropagationSystem getPropagationSystem() {
    return system;
  }

  public PointerKeyFactory getPointerKeyFactory() {
    return pointerKeyFactory;
  }

/** BEGIN Custom change: setter for pointerkey factory */
  public void setPointerKeyFactory(PointerKeyFactory pkFact) {
    pointerKeyFactory = pkFact;
  }

/** END Custom change: setter for pointerkey factory */
  public RTAContextInterpreter getContextInterpreter() {
    return contextInterpreter;
  }

  /**
   * @param caller the caller node
   * @param iKey an abstraction of the receiver of the call (or null if not applicable)
   * @return the CGNode to which this particular call should dispatch.
   */
  protected CGNode getTargetForCall(CGNode caller, CallSiteReference site, IClass recv, InstanceKey iKey[]) {

    IMethod targetMethod = options.getMethodTargetSelector().getCalleeTarget(caller, site, recv);

    // this most likely indicates an exclusion at work; the target selector
    // should have issued a warning
    if (targetMethod == null || targetMethod.isAbstract()) {
      return null;
    }
    Context targetContext = contextSelector.getCalleeTarget(caller, site, targetMethod, iKey);
    
    if (targetContext instanceof IllegalArgumentExceptionContext) {
      return null;
    }
    try {
      return getCallGraph().findOrCreateNode(targetMethod, targetContext);
    } catch (CancelException e) {
      return null;
    }
  }
  
  /**
   * @return the context selector for this call graph builder
   */
  public ContextSelector getContextSelector() {
    return contextSelector;
  }

  public void setContextSelector(ContextSelector selector) {
    contextSelector = selector;
  }

  public InstanceKeyFactory getInstanceKeys() {
    return instanceKeyFactory;
  }

  public void setInstanceKeys(InstanceKeyFactory keys) {
    this.instanceKeyFactory = keys;
  }

  /**
   * @return the InstanceKey that acts as a representative for the class of objects that includes objects allocated at the given new
   *         instruction in the given node
   */
  public InstanceKey getInstanceKeyForAllocation(CGNode node, NewSiteReference allocation) {
    return instanceKeyFactory.getInstanceKeyForAllocation(node, allocation);
  }

  /**
   * @param dim the dimension of the array whose instance we would like to model. dim == 0 represents the first dimension, e.g., the
   *          [Object; instances in [[Object; e.g., the [[Object; instances in [[[Object; dim == 1 represents the second dimension,
   *          e.g., the [Object instances in [[[Object;
   * @return the InstanceKey that acts as a representative for the class of array contents objects that includes objects allocated
   *         at the given new instruction in the given node
   */
  public InstanceKey getInstanceKeyForMultiNewArray(CGNode node, NewSiteReference allocation, int dim) {

    @Override
    return instanceKeyFactory.getInstanceKeyForMultiNewArray(node, allocation, dim);
  }

  public  InstanceKey getInstanceKeyForConstant(TypeReference type, T S) {
    return instanceKeyFactory.getInstanceKeyForConstant(type, S);
  }

  public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
    return instanceKeyFactory.getInstanceKeyForClassObject(type);
  }

  public boolean haveAlreadyVisited(CGNode node) {
    return alreadyVisited.contains(node);
  }

  protected void markAlreadyVisited(CGNode node) {
    alreadyVisited.add(node);
  }

  /**
   * record that we've discovered a node
   */
  public void markDiscovered(CGNode node) {
    discoveredNodes.add(node);
  }

  protected void markChanged(CGNode node) {
    alreadyVisited.remove(node);
    discoveredNodes.add(node);
  }

  protected boolean wasChanged(CGNode node) {
    return discoveredNodes.contains(node) && !alreadyVisited.contains(node);
  }

  /**
   * Binary op: := ArrayLoad( <arrayref>) Side effect: Creates new equations.
   */
  public final class ArrayLoadOperator extends UnarySideEffect implements IPointerOperator {
    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "ArrayLoad";
    }

    public ArrayLoadOperator(PointsToSetVariable def) {
      super(def);
      system.registerFixedSet(def, this);
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_ARRAY_LOAD) {
        PointsToSetVariable def = getFixedSet();
        String S = "EVAL ArrayLoad " + rhs.getPointerKey() + " " + def.getPointerKey();
        System.err.println(S);
        System.err.println("EVAL ArrayLoad " + def + " " + rhs);
        if (priorInstances != null) {
          continue;
          System.err.println("prior instances: " + priorInstances + " " + priorInstances.getClass());
        }
      }

      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }
      final PointerKey object = rhs.getPointerKey();

      PointsToSetVariable def = getFixedSet();
      final PointerKey dVal = def.getPointerKey();

      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!I.getConcreteType().isArrayClass()) {
            return;
          }
          TypeReference C = I.getConcreteType().getReference().getArrayElementType();
          if (C.isPrimitiveType()) {
            return;
          }
          PointerKey p = getPointerKeyForArrayContents(I);
          if (p == null) {
            return;
          }

          if (DEBUG_ARRAY_LOAD) {
            System.err.println("ArrayLoad add assign: " + dVal + " " + p);
          }
          sideEffect.b |= system.newFieldRead(dVal, assignOperator, p, object);
        }
      };
      if (priorInstances != null) {
        rhs.getValue().foreachExcluding(priorInstances, action);
        priorInstances.addAll(rhs.getValue());
      } else {
        rhs.getValue().foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }
    public int hashCode() {
      return 9871 + super.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      return super.equals(o);
    }

    @Override
    protected boolean isLoadOperator() {
      return true;
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  /**
   * Binary op: := ArrayStore( <arrayref>) Side effect: Creates new equations.
   */
  public final class ArrayStoreOperator extends UnarySideEffect implements IPointerOperator {
    @Override
    public String toString() {
      return "ArrayStore";
    }

    public ArrayStoreOperator(PointsToSetVariable val) {
      super(val);
      system.registerFixedSet(val, this);
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_ARRAY_STORE) {
        PointsToSetVariable val = getFixedSet();
        String S = "EVAL ArrayStore " + rhs.getPointerKey() + " " + val.getPointerKey();
        System.err.println(S);
        System.err.println("EVAL ArrayStore " + rhs + " " + getFixedSet());
      }

      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }
      PointerKey object = rhs.getPointerKey();

      PointsToSetVariable val = getFixedSet();
      PointerKey pVal = val.getPointerKey();

      List instances = system.getInstances(rhs.getValue());
      boolean sideEffect = false;
      for (Iterator it = instances.iterator(); it.hasNext();) {
        InstanceKey I = it.next();
        if (!I.getConcreteType().isArrayClass()) {
          continue;
        }
        if (I instanceof ZeroLengthArrayInNode) {
        }
        TypeReference C = I.getConcreteType().getReference().getArrayElementType();
        if (C.isPrimitiveType()) {
          continue;
        }
        IClass contents = getClassHierarchy().lookupClass(C);
        if (contents == null) {
          assert false : "null type for " + C + " " + I.getConcreteType();
        }
        PointerKey p = getPointerKeyForArrayContents(I);
        if (DEBUG_ARRAY_STORE) {
          System.err.println("ArrayStore add filtered-assign: " + p + " " + pVal);
        }

        // note that the following is idempotent
        if (isJavaLangObject(contents)) {
          sideEffect |= system.newFieldWrite(p, assignOperator, pVal, object);
        } else {
          sideEffect |= system.newFieldWrite(p, filterOperator, pVal, object);
        }
      }
      byte sideEffectMask = sideEffect ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    @Override
    public int hashCode() {
      return 9859 + super.hashCode();
    }

    public boolean isComplex() {
      return true;
    }

    @Override
    public boolean equals(Object o) {
      return super.equals(o);
    }

    @Override
    protected boolean isLoadOperator() {
      return false;
    }
  }

  /**
   * Binary op: := GetField( ) Side effect: Creates new equations.
   */
  public class GetFieldOperator extends UnarySideEffect implements IPointerOperator {
    private final IField field;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    public GetFieldOperator(IField field, PointsToSetVariable def) {
      super(def);
      this.field = field;
      system.registerFixedSet(def, this);
    }

    @Override
    public String toString() {
      return "GetField " + getField() + "," + getFixedSet().getPointerKey();
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_GET) {
        String S = "EVAL GetField " + getField() + " " + getFixedSet().getPointerKey() + " " + rhs.getPointerKey() + getFixedSet()
            + " " + rhs;
        System.err.println(S);
      }

      PointsToSetVariable ref = rhs;
      if (ref.size() == 0) {
        return NOT_CHANGED;
      }
      final PointerKey object = ref.getPointerKey();
      PointsToSetVariable def = getFixedSet();
      final PointerKey dVal = def.getPointerKey();

      IntSet value = filterInstances(ref.getValue());
      if (DEBUG_GET) {
        System.err.println("filtered value: " + value + " " + value.getClass());
        if (priorInstances != null) {
          System.err.println("prior instances: " + priorInstances + " " + priorInstances.getClass());
        }
      }
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!representsNullType(I)) {
            PointerKey p = getPointerKeyForInstanceField(I, getField());

            if (p != null) {
              if (DEBUG_GET) {
                String S = "Getfield add constraint " + dVal + " " + p;
                System.err.println(S);
              }
              sideEffect.b |= system.newFieldRead(dVal, assignOperator, p, object);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    /**
     * Subclasses can override as needed
     */
    protected IntSet filterInstances(IntSet value) {
      return value;
    }

    @Override
    public int hashCode() {
      return 9857 * getField().hashCode() + getFixedSet().hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof GetFieldOperator) {
        GetFieldOperator other = (GetFieldOperator) o;
        return getField().equals(other.getField()) && getFixedSet().equals(other.getFixedSet());
      } else {
        return false;
      }
    }

    protected IField getField() {
      return field;
    }

    @Override
    protected boolean isLoadOperator() {
      return true;
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  /**
   * Operator that represents a putfield
   */
  public class PutFieldOperator extends UnarySideEffect implements IPointerOperator {
    private final IField field;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "PutField" + getField();
    }

    public PutFieldOperator(IField field, PointsToSetVariable val) {
      super(val);
      this.field = field;
      system.registerFixedSet(val, this);
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_PUT) {
        String S = "EVAL PutField " + getField() + " " + (getFixedSet()).getPointerKey() + " " + rhs.getPointerKey()
            + getFixedSet() + " " + rhs;
        System.err.println(S);
      }

      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }
      final PointerKey object = rhs.getPointerKey();

      PointsToSetVariable val = getFixedSet();
      final PointerKey pVal = val.getPointerKey();
      IntSet value = rhs.getValue();
      value = filterInstances(value);
      final UnaryOperator assign = getPutAssignmentOperator();
      if (assign == null) {
        Assertions.UNREACHABLE();
      }
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!representsNullType(I)) {
            if (DEBUG_PUT) {
              String S = "Putfield consider instance " + I;
              System.err.println(S);
            }
            PointerKey p = getPointerKeyForInstanceField(I, getField());
            if (p != null) {
              if (DEBUG_PUT) {
                String S = "Putfield add constraint " + p + " " + pVal;
                System.err.println(S);
              }
              sideEffect.b |= system.newFieldWrite(p, assign, pVal, object);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    /**
     * Subclasses can override as needed
     */
    protected IntSet filterInstances(IntSet value) {
      return value;
    }

    @Override
    public int hashCode() {
      return 9857 * getField().hashCode() + getFixedSet().hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o != null && o.getClass().equals(getClass())) {
        PutFieldOperator other = (PutFieldOperator) o;
        return getField().equals(other.getField()) && getFixedSet().equals(other.getFixedSet());
      } else {
        return false;
      }
    }

    /**

    public InstanceArrayStoreOperator(InstanceKey instance) {
     * subclasses (e.g. XTA) can override this to enforce a filtered assignment. returns null if there's a problem.
     */
    public UnaryOperator getPutAssignmentOperator() {
      return assignOperator;
    }

    /**
     * @return Returns the field.
     */
    protected IField getField() {
      return field;
    }

    @Override
    protected boolean isLoadOperator() {
      return false;
    }
  }

  /**
   * Update the points-to-set for a field to include a particular instance key.
   */
  public final class InstancePutFieldOperator extends UnaryOperator implements IPointerOperator {
    final private IField field;

    final private InstanceKey instance;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "InstancePutField" + field;
    }

    public InstancePutFieldOperator(IField field, InstanceKey instance) {
      this.field = field;
      this.instance = instance;
    }

    /**
     * Simply add the instance to each relevant points-to set.
     */
    @Override
    public byte evaluate(PointsToSetVariable dummyLHS, PointsToSetVariable var) {
      PointsToSetVariable ref = var;
      if (ref.size() == 0) {
        return NOT_CHANGED;
      }
      IntSet value = ref.getValue();
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!representsNullType(I)) {
            PointerKey p = getPointerKeyForInstanceField(I, field);
            if (p != null) {
              sideEffect.b |= system.newConstraint(p, instance);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    @Override
    public int hashCode() {
      return field.hashCode() + 9839 * instance.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof InstancePutFieldOperator) {
        InstancePutFieldOperator other = (InstancePutFieldOperator) o;
        return field.equals(other.field) && instance.equals(other.instance);
      } else {
        return false;
      }
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  /**
   * Update the points-to-set for an array contents to include a particular instance key.
   */
  public final class InstanceArrayStoreOperator extends UnaryOperator implements IPointerOperator {
    final private InstanceKey instance;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "InstanceArrayStore ";
    }
      this.instance = instance;
    }

    /**
     * Simply add the instance to each relevant points-to set.
     */
    @Override
    public byte evaluate(PointsToSetVariable dummyLHS, PointsToSetVariable var) {
      PointsToSetVariable arrayref = var;
      if (arrayref.size() == 0) {
        return NOT_CHANGED;
      }
      IntSet value = arrayref.getValue();
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!I.getConcreteType().isArrayClass()) {
            return;
          }
          if (I instanceof ZeroLengthArrayInNode) {
            return;
          }
          TypeReference C = I.getConcreteType().getReference().getArrayElementType();
          if (C.isPrimitiveType()) {
            return;
          }
          IClass contents = getClassHierarchy().lookupClass(C);
          if (contents == null) {
            assert false : "null type for " + C + " " + I.getConcreteType();
          }
          PointerKey p = getPointerKeyForArrayContents(I);
          if (contents.isInterface()) {
            if (getClassHierarchy().implementsInterface(instance.getConcreteType(), contents)) {
              sideEffect.b |= system.newConstraint(p, instance);
            }
          } else {
            if (getClassHierarchy().isSubclassOf(instance.getConcreteType(), contents)) {
              sideEffect.b |= system.newConstraint(p, instance);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    @Override
    public int hashCode() {
      return 9839 * instance.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof InstanceArrayStoreOperator) {
        InstanceArrayStoreOperator other = (InstanceArrayStoreOperator) o;
        return instance.equals(other.instance);
      } else {
        return false;
      }
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  protected MutableIntSet getMutableInstanceKeysForClass(IClass klass) {
    return system.cloneInstanceKeysForClass(klass);
  }

  protected IntSet getInstanceKeysForClass(IClass klass) {
    return system.getInstanceKeysForClass(klass);
  }

  /**
   * @param klass a class
   * @return an int set which represents the subset of S that correspond to subtypes of klass
   */
  protected IntSet filterForClass(IntSet S, IClass klass) {
    MutableIntSet filter = null;
    if (klass.getReference().equals(TypeReference.JavaLangObject)) {
      return S;
    } else {
      filter = getMutableInstanceKeysForClass(klass);

      boolean debug = false;
      if (DEBUG_FILTER) {
        String s = "klass     " + klass;
        System.err.println(s);
        System.err.println("initial filter    " + filter);
      }
      filter.intersectWith(S);

      if (DEBUG_FILTER && debug) {
        System.err.println("final filter    " + filter);
      }
    }
    return filter;
  }

  protected class InverseFilterOperator extends FilterOperator {
    public InverseFilterOperator() {
      super();
    }

    @Override
    public String toString() {
      return "InverseFilter";
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    @Override
    public boolean isComplex() {
      return false;
    }

    /*
     * simply check if rhs contains a malleable.
     * 
     * @see com.ibm.wala.dataflow.UnaryOperator#evaluate(com.ibm.wala.dataflow.IVariable, com.ibm.wala.dataflow.IVariable)
     */
    @Override
    public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable rhs) {

      FilteredPointerKey pk = (FilteredPointerKey) lhs.getPointerKey();
      FilteredPointerKey.TypeFilter filter = pk.getTypeFilter();

      boolean debug = false;
      if (DEBUG_FILTER) {
        String S = "EVAL InverseFilter/" + filter + " " + lhs.getPointerKey() + " " + rhs.getPointerKey();
        S += "\nEVAL      " + lhs + " " + rhs;
        System.err.println(S);
      }
      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }

      boolean changed = filter.addInverseFiltered(system, lhs, rhs);

      if (DEBUG_FILTER) {
        if (debug) {
          System.err.println("RESULT " + lhs + (changed ? " (changed)" : ""));
        }
      }
      return changed ? CHANGED : NOT_CHANGED;
    }
  }

  protected IPointsToSolver getSolver() {
    return solver;
  }

  /**

   * Add constraints when the interpretation of a node changes (e.g. reflection)
   * @param monitor 
   * @throws CancelException 
   */
  public void addConstraintsFromChangedNode(CGNode node, IProgressMonitor monitor) throws CancelException {
    unconditionallyAddConstraintsFromNode(node, monitor);
  }

  protected abstract boolean unconditionallyAddConstraintsFromNode(CGNode node, IProgressMonitor monitor) throws CancelException;

  protected static class MutableBoolean {
    // a horrendous hack since we don't have closures
    boolean b = false;
  }

  public AnalysisCache getAnalysisCache() {
    return analysisCache;
  };

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.propagation;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.ibm.wala.analysis.reflection.IllegalArgumentExceptionContext;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticClass;
import com.ibm.wala.fixpoint.UnaryOperator;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.CallGraphBuilder;
import com.ibm.wala.ipa.callgraph.CallGraphBuilderCancelException;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.CancelRuntimeException;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetAction;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * This abstract base class provides the general algorithm for a call graph builder that relies on propagation through an iterative
 * dataflow solver
 * 
 * TODO: This implementation currently keeps all points to sets live ... even those for local variables that do not span
 * interprocedural boundaries. This may be too space-inefficient .. we can consider recomputing local sets on demand.
 */
public abstract class PropagationCallGraphBuilder implements CallGraphBuilder {
  private final static boolean DEBUG_ALL = false;

  final static boolean DEBUG_ASSIGN = DEBUG_ALL | false;

  private final static boolean DEBUG_ARRAY_LOAD = DEBUG_ALL | false;

  private final static boolean DEBUG_ARRAY_STORE = DEBUG_ALL | false;

  private final static boolean DEBUG_FILTER = DEBUG_ALL | false;
  final protected static boolean DEBUG_GENERAL = DEBUG_ALL | false;

  private final static boolean DEBUG_GET = DEBUG_ALL | false;

  private final static boolean DEBUG_PUT = DEBUG_ALL | false;

  private final static boolean DEBUG_ENTRYPOINTS = DEBUG_ALL | false;

  /**
   * Meta-data regarding how pointers are modeled
   */
  protected final PointerKeyFactory pointerKeyFactory;

  /**
   * The object that represents the java.lang.Object class
   */
  final private IClass JAVA_LANG_OBJECT;

  /**
   * Governing class hierarchy
   */
  final protected IClassHierarchy cha;

  /**
   * Special rules for bypassing Java calls
   */
  final protected AnalysisOptions options;

  /**
   * Cache of IRs and things
   */
  private final AnalysisCache analysisCache;

  /**
   * Set of nodes that have already been traversed for constraints
   */
  final private Set alreadyVisited = HashSetFactory.make();

  /**
   * At any given time, the set of nodes that have been discovered but not yet processed for constraints
   */
  private Set discoveredNodes = HashSetFactory.make();

  /**
   * Set of calls (CallSiteReferences) that are created by entrypoints
   */
  final protected Set entrypointCallSites = HashSetFactory.make();

  /**
   * The system of constraints used to build this graph
   */
  protected PropagationSystem system;

  /**
   * Algorithm used to solve the system of constraints
   */
  private IPointsToSolver solver;

  /**
   * The call graph under construction
   */
  protected final ExplicitCallGraph callGraph;

  /**
   * Singleton operator for assignments
   */
  protected final static AssignOperator assignOperator = new AssignOperator();

  /**
   * singleton operator for filter
   */
  public final FilterOperator filterOperator = new FilterOperator();

  /**
   * singleton operator for inverse filter
   */
  protected final InverseFilterOperator inverseFilterOperator = new InverseFilterOperator();

  /**
   * An object which interprets methods in context
   */
  private SSAContextInterpreter contextInterpreter;

  /**
   * A context selector which may use information derived from the propagation-based dataflow.
   */
  protected ContextSelector contextSelector;

  /**
   * An object that abstracts how to model instances in the heap.
   */
  protected InstanceKeyFactory instanceKeyFactory;

  /**
   * Algorithmic choice: should the GetfieldOperator and PutfieldOperator cache its previous history to reduce work?
   */
  final private boolean rememberGetPutHistory = true;

  /**
   * @param cha governing class hierarchy
   * @param options governing call graph construction options
   * @param pointerKeyFactory factory which embodies pointer abstraction policy
   */
  protected PropagationCallGraphBuilder(IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache,
      PointerKeyFactory pointerKeyFactory) {
    if (cha == null) {
      throw new IllegalArgumentException("cha is null");
    }
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    assert cache != null;
    this.cha = cha;
    this.options = options;
    this.analysisCache = cache;
    // we need pointer keys to handle reflection
    assert pointerKeyFactory != null;
    this.pointerKeyFactory = pointerKeyFactory;
    callGraph = createEmptyCallGraph(cha, options);
    try {
      callGraph.init();
    } catch (CancelException e) {
      if (DEBUG_GENERAL) {
        System.err.println("Could not initialize the call graph due to node number constraints: " + e.getMessage());
      }
    }
    callGraph.setInterpreter(contextInterpreter);
    JAVA_LANG_OBJECT = cha.lookupClass(TypeReference.JavaLangObject);
  }

  protected ExplicitCallGraph createEmptyCallGraph(IClassHierarchy cha, AnalysisOptions options) {
    return new ExplicitCallGraph(cha, options, getAnalysisCache());
  }

  /**
   * @return true iff the klass represents java.lang.Object
   */
  protected boolean isJavaLangObject(IClass klass) {
    return (klass.getReference().equals(TypeReference.JavaLangObject));
  }

  public CallGraph makeCallGraph(AnalysisOptions options) throws IllegalArgumentException, CancelException {
    return makeCallGraph(options, null);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.CallGraphBuilder#makeCallGraph(com.ibm.wala.ipa.callgraph.AnalysisOptions)
   */
  public CallGraph makeCallGraph(AnalysisOptions options, IProgressMonitor monitor) throws IllegalArgumentException,
      CallGraphBuilderCancelException {
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    system = makeSystem(options);

    if (DEBUG_GENERAL) {
      System.err.println("Enter makeCallGraph!");
    }

    if (DEBUG_GENERAL) {
      System.err.println("Initialized call graph");
    }

    system.setMinEquationsForTopSort(options.getMinEquationsForTopSort());
    system.setTopologicalGrowthFactor(options.getTopologicalGrowthFactor());
    system.setMaxEvalBetweenTopo(options.getMaxEvalBetweenTopo());

    discoveredNodes = HashSetFactory.make();
    discoveredNodes.add(callGraph.getFakeRootNode());

    // Set up the initially reachable methods and classes
    for (Iterator it = options.getEntrypoints().iterator(); it.hasNext();) {
      Entrypoint E = (Entrypoint) it.next();
      if (DEBUG_ENTRYPOINTS) {
        System.err.println("Entrypoint: " + E);
      }
      SSAAbstractInvokeInstruction call = E.addCall((AbstractRootMethod) callGraph.getFakeRootNode().getMethod());

      if (call == null) {
        Warnings.add(EntrypointResolutionWarning.create(E));
      } else {
        entrypointCallSites.add(call.getCallSite());
      }
    }

    customInit();

    solver = makeSolver();
    try {
      solver.solve(monitor);
    } catch (CancelException e) {
      CallGraphBuilderCancelException c = CallGraphBuilderCancelException.createCallGraphBuilderCancelException(e, callGraph,
          system.extractPointerAnalysis(this));
      throw c;
    } catch (CancelRuntimeException e) {
      CallGraphBuilderCancelException c = CallGraphBuilderCancelException.createCallGraphBuilderCancelException(e, callGraph,
          system.extractPointerAnalysis(this));
      throw c;
    }

    return callGraph;
  }

  protected PropagationSystem makeSystem(AnalysisOptions options) {
    return new PropagationSystem(callGraph, pointerKeyFactory, instanceKeyFactory);
  }

  protected abstract IPointsToSolver makeSolver();

  /**
   * A warning for when we fail to resolve a call to an entrypoint
   */
  private static class EntrypointResolutionWarning extends Warning {

    final Entrypoint entrypoint;

    EntrypointResolutionWarning(Entrypoint entrypoint) {
      super(Warning.SEVERE);
      this.entrypoint = entrypoint;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + entrypoint;
    }

    public static EntrypointResolutionWarning create(Entrypoint entrypoint) {
      return new EntrypointResolutionWarning(entrypoint);
    }
  }

  protected void customInit() {
  }

  /**
   * Add constraints for a node.
   * @param monitor 
   * 
   * @return true iff any new constraints are added.
   */
  protected abstract boolean addConstraintsFromNode(CGNode n, IProgressMonitor monitor) throws CancelException;

  /**
   * Add constraints from newly discovered nodes. Note: the act of adding constraints may discover new nodes, so this routine is
   * iterative.
   * 
   * @return true iff any new constraints are added.
   * @throws CancelException 
   */
  protected boolean addConstraintsFromNewNodes(IProgressMonitor monitor) throws CancelException {
    boolean result = false;
    while (!discoveredNodes.isEmpty()) {
      Iterator it = discoveredNodes.iterator();
      discoveredNodes = HashSetFactory.make();
      while (it.hasNext()) {
        CGNode n = it.next();
        result |= addConstraintsFromNode(n, monitor);
      }
    }
    return result;
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the local variable identified by
   *         the value number parameter.
   */
  public PointerKey getPointerKeyForLocal(CGNode node, int valueNumber) {
    return pointerKeyFactory.getPointerKeyForLocal(node, valueNumber);
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the local variable identified by
   *         the value number parameter.
   */
  public FilteredPointerKey getFilteredPointerKeyForLocal(CGNode node, int valueNumber, FilteredPointerKey.TypeFilter filter) {
    assert filter != null;
    return pointerKeyFactory.getFilteredPointerKeyForLocal(node, valueNumber, filter);
  }

  public FilteredPointerKey getFilteredPointerKeyForLocal(CGNode node, int valueNumber, IClass filter) {
    return getFilteredPointerKeyForLocal(node, valueNumber, new FilteredPointerKey.SingleClassFilter(filter));
  }

  public FilteredPointerKey getFilteredPointerKeyForLocal(CGNode node, int valueNumber, InstanceKey filter) {
    return getFilteredPointerKeyForLocal(node, valueNumber, new FilteredPointerKey.SingleInstanceFilter(filter));
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the return value for a node
   */
  public PointerKey getPointerKeyForReturnValue(CGNode node) {
    return pointerKeyFactory.getPointerKeyForReturnValue(node);
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the exceptional return value
   */
  public PointerKey getPointerKeyForExceptionalReturnValue(CGNode node) {
    return pointerKeyFactory.getPointerKeyForExceptionalReturnValue(node);
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the contents of the static field
   */
  public PointerKey getPointerKeyForStaticField(IField f) {
    assert f != null : "null FieldReference";
    return pointerKeyFactory.getPointerKeyForStaticField(f);
  }

  /**
   * @return the PointerKey that acts as a representation for the class of pointers that includes the given instance field. null if
   *         there's some problem.
   * @throws IllegalArgumentException if I is null
   * @throws IllegalArgumentException if field is null
   */
  public PointerKey getPointerKeyForInstanceField(InstanceKey I, IField field) {
    if (field == null) {
      throw new IllegalArgumentException("field is null");
    }
    if (I == null) {
      throw new IllegalArgumentException("I is null");
    }
    IClass t = field.getDeclaringClass();
    IClass C = I.getConcreteType();
    if (!(C instanceof SyntheticClass)) {
      if (!getClassHierarchy().isSubclassOf(C, t)) {
        return null;
      }
    }

    return pointerKeyFactory.getPointerKeyForInstanceField(I, field);
  }

  /**
   * TODO: expand this API to differentiate between different array indices
   * 
   * @param I an InstanceKey representing an abstract array
   * @return the PointerKey that acts as a representation for the class of pointers that includes the given array contents, or null
   *         if none found.
   * @throws IllegalArgumentException if I is null
   */
  public PointerKey getPointerKeyForArrayContents(InstanceKey I) {
    if (I == null) {
      throw new IllegalArgumentException("I is null");
    }
    IClass C = I.getConcreteType();
    if (!C.isArrayClass()) {
      assert false : "illegal arguments: " + I;
    }
    return pointerKeyFactory.getPointerKeyForArrayContents(I);
  }

  /**
   * Handle assign of a particular exception instance into an exception variable
   * 
   * @param exceptionVar points-to set for a variable representing a caught exception
   * @param catchClasses set of TypeReferences that the exceptionVar may catch
   * @param e a particular exception instance
   */
  protected void assignInstanceToCatch(PointerKey exceptionVar, Set catchClasses, InstanceKey e) {
    if (catches(catchClasses, e.getConcreteType(), cha)) {
      system.newConstraint(exceptionVar, e);
    }
  }

  /**
   * Generate a set of constraints to represent assignment to an exception variable in a catch clause. Note that we use
   * FilterOperator to filter out types that the exception handler doesn't catch.
   * 
   * @param exceptionVar points-to set for a variable representing a caught exception
   * @param catchClasses set of TypeReferences that the exceptionVar may catch
   * @param e points-to-set representing a thrown exception that might be caught.
   */
  protected void addAssignmentsForCatchPointerKey(PointerKey exceptionVar, Set catchClasses, PointerKey e) {
    if (DEBUG_GENERAL) {
      System.err.println("addAssignmentsForCatch: " + catchClasses);
    }
    // this is tricky ... we want to filter based on a number of classes ... so we can't
    // just used a FilteredPointerKey for the exceptionVar. Instead, we create a new
    // "typed local" for each catch class, and coalesce the results using
    // assignment
    for (IClass c : catchClasses) {
      if (c.getReference().equals(c.getClassLoader().getLanguage().getThrowableType())) {
        system.newConstraint(exceptionVar, assignOperator, e);
      } else {
        FilteredPointerKey typedException = TypedPointerKey.make(exceptionVar, c);
        system.newConstraint(typedException, filterOperator, e);
        system.newConstraint(exceptionVar, assignOperator, typedException);
      }
    }
  }

  /**
   * A warning for when we fail to resolve a call to an entrypoint
   */
  @SuppressWarnings("unused")
  private static class ExceptionLookupFailure extends Warning {

    final TypeReference t;

    ExceptionLookupFailure(TypeReference t) {
      super(Warning.SEVERE);
      this.t = t;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + t;
    }

    public static ExceptionLookupFailure create(TypeReference t) {
      return new ExceptionLookupFailure(t);
    }
  }

  /**
   * A pointer key that delegates to an untyped variant, but adds a type filter
   */
  public final static class TypedPointerKey implements FilteredPointerKey {

    private final IClass type;

    private final PointerKey base;

    static TypedPointerKey make(PointerKey base, IClass type) {
      assert type != null;
      return new TypedPointerKey(base, type);
    }

    private TypedPointerKey(PointerKey base, IClass type) {
      this.type = type;
      this.base = base;
      assert type != null;
      assert !(type instanceof FilteredPointerKey);
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey#getTypeFilter()
     */
    public TypeFilter getTypeFilter() {
      return new SingleClassFilter(type);
    }

    @Override
    public boolean equals(Object obj) {
      // instanceof is OK because this class is final
      if (obj instanceof TypedPointerKey) {
        TypedPointerKey other = (TypedPointerKey) obj;
        return type.equals(other.type) && base.equals(other.base);
      } else {
        return false;
      }
    }

    @Override
    public int hashCode() {
      return 67931 * base.hashCode() + type.hashCode();
    }

    @Override
    public String toString() {
      return "{ " + base + " type: " + type + "}";
    }

    public PointerKey getBase() {
      return base;
    }
  }

  /**
   * @param catchClasses Set of TypeReference
   * @param klass an Exception Class
   * @return true iff klass is a subclass of some element of the Set
   * @throws IllegalArgumentException if catchClasses is null
   */
  public static boolean catches(Set catchClasses, IClass klass, IClassHierarchy cha) {
    if (catchClasses == null) {
      throw new IllegalArgumentException("catchClasses is null");
    }
    // quick shortcut
    if (catchClasses.size() == 1) {
      IClass c = catchClasses.iterator().next();
      if (c != null && c.getReference().equals(TypeReference.JavaLangThread)) {
        return true;
      }
    }
    for (IClass c : catchClasses) {
      if (c != null && cha.isAssignableFrom(c, klass)) {
        return true;
      }
    }
    return false;
  }

  public static boolean representsNullType(InstanceKey key) throws IllegalArgumentException {
    if (key == null) {
      throw new IllegalArgumentException("key == null");
    }
    IClass cls = key.getConcreteType();
    Language L = cls.getClassLoader().getLanguage();
    return L.isNullType(cls.getReference());
  }

  /**
  public RTAContextInterpreter getContextInterpreter() {
   * The FilterOperator is a filtered set-union. i.e. the LHS is `unioned' with the RHS, but filtered by the set associated with
   * this operator instance. The filter is the set of InstanceKeys corresponding to the target type of this cast. This is still
   * monotonic.
   * 
   * LHS U= (RHS n k)
   * 
   * 
   * Unary op: := Cast_k( )
   * 
   * (Again, technically a binary op -- see note for Assign)
   * 
   * TODO: these need to be canonicalized.
   * 
   */
  public class FilterOperator extends UnaryOperator implements IPointerOperator {

    protected FilterOperator() {
    }

    /*
     * @see com.ibm.wala.dataflow.UnaryOperator#evaluate(com.ibm.wala.dataflow.IVariable, com.ibm.wala.dataflow.IVariable)
     */
    @Override
    public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable rhs) {

      FilteredPointerKey pk = (FilteredPointerKey) lhs.getPointerKey();

      if (DEBUG_FILTER) {
        String S = "EVAL Filter " + lhs.getPointerKey() + " " + rhs.getPointerKey();
        S += "\nEVAL      " + lhs + " " + rhs;
        System.err.println(S);
      }
      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }

      boolean changed = false;
      FilteredPointerKey.TypeFilter filter = pk.getTypeFilter();
      changed = filter.addFiltered(system, lhs, rhs);

      if (DEBUG_FILTER) {
        System.err.println("RESULT " + lhs + (changed ? " (changed)" : ""));
      }

      return changed ? CHANGED : NOT_CHANGED;
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return false;
    }

    @Override
    public String toString() {
      return "Filter ";
    }

    @Override
    public boolean equals(Object obj) {
      // these objects are canonicalized for the duration of a solve
      return this == obj;
    }

    @Override
    public int hashCode() {
      return 88651;
    }

  }

  public IClassHierarchy getClassHierarchy() {
    return cha;
  }

  public AnalysisOptions getOptions() {
    return options;
  }

  public IClass getJavaLangObject() {
    return JAVA_LANG_OBJECT;
  }

  public ExplicitCallGraph getCallGraph() {
    return callGraph;
  }

  /**
   * Subclasses must register the context interpreter before building a call graph.
   */
  public void setContextInterpreter(SSAContextInterpreter interpreter) {
    contextInterpreter = interpreter;
    callGraph.setInterpreter(interpreter);
  }

  /*
   * @see com.ibm.detox.ipa.callgraph.CallGraphBuilder#getPointerAnalysis()
   */
  public PointerAnalysis getPointerAnalysis() {
    return system.extractPointerAnalysis(this);
  }

  public PropagationSystem getPropagationSystem() {
    return system;
  }

  public PointerKeyFactory getPointerKeyFactory() {
    return pointerKeyFactory;
  }

    return contextInterpreter;
  }

  /**
   * @param caller the caller node
   * @param iKey an abstraction of the receiver of the call (or null if not applicable)
   * @return the CGNode to which this particular call should dispatch.
   */
  protected CGNode getTargetForCall(CGNode caller, CallSiteReference site, IClass recv, InstanceKey iKey[]) {

    IMethod targetMethod = options.getMethodTargetSelector().getCalleeTarget(caller, site, recv);

    // this most likely indicates an exclusion at work; the target selector
    // should have issued a warning
    if (targetMethod == null || targetMethod.isAbstract()) {
      return null;
    }
    Context targetContext = contextSelector.getCalleeTarget(caller, site, targetMethod, iKey);
    
    if (targetContext instanceof IllegalArgumentExceptionContext) {
      return null;
    }
    try {
      return getCallGraph().findOrCreateNode(targetMethod, targetContext);
    } catch (CancelException e) {
      return null;
    }
  }
  
  /**
   * @return the context selector for this call graph builder
   */
  public ContextSelector getContextSelector() {
    return contextSelector;
  }

  public void setContextSelector(ContextSelector selector) {
    contextSelector = selector;
  }

  public InstanceKeyFactory getInstanceKeys() {
    return instanceKeyFactory;
  }

  public void setInstanceKeys(InstanceKeyFactory keys) {
    this.instanceKeyFactory = keys;
  }

  /**
   * @return the InstanceKey that acts as a representative for the class of objects that includes objects allocated at the given new
   *         instruction in the given node
   */
  public InstanceKey getInstanceKeyForAllocation(CGNode node, NewSiteReference allocation) {
    return instanceKeyFactory.getInstanceKeyForAllocation(node, allocation);
  }

  /**
   * @param dim the dimension of the array whose instance we would like to model. dim == 0 represents the first dimension, e.g., the
   *          [Object; instances in [[Object; e.g., the [[Object; instances in [[[Object; dim == 1 represents the second dimension,
   *          e.g., the [Object instances in [[[Object;
   * @return the InstanceKey that acts as a representative for the class of array contents objects that includes objects allocated
   *         at the given new instruction in the given node
   */
  public InstanceKey getInstanceKeyForMultiNewArray(CGNode node, NewSiteReference allocation, int dim) {
    return instanceKeyFactory.getInstanceKeyForMultiNewArray(node, allocation, dim);
  }

  public  InstanceKey getInstanceKeyForConstant(TypeReference type, T S) {
    return instanceKeyFactory.getInstanceKeyForConstant(type, S);
  }

  public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
    return instanceKeyFactory.getInstanceKeyForClassObject(type);
  }

  public boolean haveAlreadyVisited(CGNode node) {
    return alreadyVisited.contains(node);
  }

  protected void markAlreadyVisited(CGNode node) {
    alreadyVisited.add(node);
  }

  /**
   * record that we've discovered a node
   */
  public void markDiscovered(CGNode node) {
    discoveredNodes.add(node);
  }

  protected void markChanged(CGNode node) {
    alreadyVisited.remove(node);
    discoveredNodes.add(node);
  }

  protected boolean wasChanged(CGNode node) {
    return discoveredNodes.contains(node) && !alreadyVisited.contains(node);
  }

  /**
   * Binary op: := ArrayLoad( <arrayref>) Side effect: Creates new equations.
   */
  public final class ArrayLoadOperator extends UnarySideEffect implements IPointerOperator {
    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "ArrayLoad";
    }

    public ArrayLoadOperator(PointsToSetVariable def) {
      super(def);
      system.registerFixedSet(def, this);
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_ARRAY_LOAD) {
        PointsToSetVariable def = getFixedSet();
        String S = "EVAL ArrayLoad " + rhs.getPointerKey() + " " + def.getPointerKey();
        System.err.println(S);
        System.err.println("EVAL ArrayLoad " + def + " " + rhs);
        if (priorInstances != null) {
          System.err.println("prior instances: " + priorInstances + " " + priorInstances.getClass());
        }
      }

      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }
      final PointerKey object = rhs.getPointerKey();

      PointsToSetVariable def = getFixedSet();
      final PointerKey dVal = def.getPointerKey();

      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!I.getConcreteType().isArrayClass()) {
            return;
          }
          TypeReference C = I.getConcreteType().getReference().getArrayElementType();
          if (C.isPrimitiveType()) {
            return;
          }
          PointerKey p = getPointerKeyForArrayContents(I);
          if (p == null) {
            return;
          }

          if (DEBUG_ARRAY_LOAD) {
            System.err.println("ArrayLoad add assign: " + dVal + " " + p);
          }
          sideEffect.b |= system.newFieldRead(dVal, assignOperator, p, object);
        }
      };
      if (priorInstances != null) {
        rhs.getValue().foreachExcluding(priorInstances, action);
        priorInstances.addAll(rhs.getValue());
      } else {
        rhs.getValue().foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    @Override
    public int hashCode() {
      return 9871 + super.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      return super.equals(o);
    }

    @Override
    protected boolean isLoadOperator() {
      return true;
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  /**
   * Binary op: := ArrayStore( <arrayref>) Side effect: Creates new equations.
   */
  public final class ArrayStoreOperator extends UnarySideEffect implements IPointerOperator {
    @Override
    public String toString() {
      return "ArrayStore";
    }

    public ArrayStoreOperator(PointsToSetVariable val) {
      super(val);
      system.registerFixedSet(val, this);
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_ARRAY_STORE) {
        PointsToSetVariable val = getFixedSet();
        String S = "EVAL ArrayStore " + rhs.getPointerKey() + " " + val.getPointerKey();
        System.err.println(S);
        System.err.println("EVAL ArrayStore " + rhs + " " + getFixedSet());
      }

      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }
      PointerKey object = rhs.getPointerKey();

      PointsToSetVariable val = getFixedSet();
      PointerKey pVal = val.getPointerKey();

      List instances = system.getInstances(rhs.getValue());
      boolean sideEffect = false;
      for (Iterator it = instances.iterator(); it.hasNext();) {
        InstanceKey I = it.next();
        if (!I.getConcreteType().isArrayClass()) {
          continue;
        }
        if (I instanceof ZeroLengthArrayInNode) {
          continue;
        }
        TypeReference C = I.getConcreteType().getReference().getArrayElementType();
        if (C.isPrimitiveType()) {
          continue;
        }
        IClass contents = getClassHierarchy().lookupClass(C);
        if (contents == null) {
          assert false : "null type for " + C + " " + I.getConcreteType();
        }
        PointerKey p = getPointerKeyForArrayContents(I);
        if (DEBUG_ARRAY_STORE) {
          System.err.println("ArrayStore add filtered-assign: " + p + " " + pVal);
        }

        // note that the following is idempotent
    }
        if (isJavaLangObject(contents)) {
          sideEffect |= system.newFieldWrite(p, assignOperator, pVal, object);
        } else {
          sideEffect |= system.newFieldWrite(p, filterOperator, pVal, object);
        }
      }
      byte sideEffectMask = sideEffect ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    @Override
    public int hashCode() {
      return 9859 + super.hashCode();
    }

    public boolean isComplex() {
      return true;
    }

    @Override
    public boolean equals(Object o) {
      return super.equals(o);
    }

    @Override
    protected boolean isLoadOperator() {
      return false;
    }
  }

  /**
   * Binary op: := GetField( ) Side effect: Creates new equations.
   */
  public class GetFieldOperator extends UnarySideEffect implements IPointerOperator {
    private final IField field;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    public GetFieldOperator(IField field, PointsToSetVariable def) {
      super(def);
      this.field = field;
      system.registerFixedSet(def, this);
    }

    @Override
    public String toString() {
      return "GetField " + getField() + "," + getFixedSet().getPointerKey();
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_GET) {
        String S = "EVAL GetField " + getField() + " " + getFixedSet().getPointerKey() + " " + rhs.getPointerKey() + getFixedSet()
            + " " + rhs;
        System.err.println(S);
      }

      PointsToSetVariable ref = rhs;
      if (ref.size() == 0) {
        return NOT_CHANGED;
      }
      final PointerKey object = ref.getPointerKey();
      PointsToSetVariable def = getFixedSet();
      final PointerKey dVal = def.getPointerKey();

      IntSet value = filterInstances(ref.getValue());
      if (DEBUG_GET) {
        System.err.println("filtered value: " + value + " " + value.getClass());
        if (priorInstances != null) {
          System.err.println("prior instances: " + priorInstances + " " + priorInstances.getClass());
        }
      }
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!representsNullType(I)) {
            PointerKey p = getPointerKeyForInstanceField(I, getField());

            if (p != null) {
              if (DEBUG_GET) {
                String S = "Getfield add constraint " + dVal + " " + p;
                System.err.println(S);
              }
              sideEffect.b |= system.newFieldRead(dVal, assignOperator, p, object);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    /**
     * Subclasses can override as needed
     */
    protected IntSet filterInstances(IntSet value) {
      return value;

    @Override
    public int hashCode() {
      return 9857 * getField().hashCode() + getFixedSet().hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof GetFieldOperator) {
        GetFieldOperator other = (GetFieldOperator) o;
        return getField().equals(other.getField()) && getFixedSet().equals(other.getFixedSet());
      } else {
        return false;
      }
    }

    protected IField getField() {
      return field;
    }

    @Override
    protected boolean isLoadOperator() {
      return true;
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  /**
   * Operator that represents a putfield
   */
  public class PutFieldOperator extends UnarySideEffect implements IPointerOperator {
    private final IField field;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "PutField" + getField();
    }

    public PutFieldOperator(IField field, PointsToSetVariable val) {
      super(val);
      this.field = field;
      system.registerFixedSet(val, this);
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_PUT) {
        String S = "EVAL PutField " + getField() + " " + (getFixedSet()).getPointerKey() + " " + rhs.getPointerKey()
            + getFixedSet() + " " + rhs;
        System.err.println(S);
      }

      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }
      final PointerKey object = rhs.getPointerKey();

      PointsToSetVariable val = getFixedSet();
      final PointerKey pVal = val.getPointerKey();
      IntSet value = rhs.getValue();
      value = filterInstances(value);
      final UnaryOperator assign = getPutAssignmentOperator();
      if (assign == null) {
        Assertions.UNREACHABLE();
      }
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!representsNullType(I)) {
            if (DEBUG_PUT) {
              String S = "Putfield consider instance " + I;
              System.err.println(S);
            }
            PointerKey p = getPointerKeyForInstanceField(I, getField());
            if (p != null) {
              if (DEBUG_PUT) {
                String S = "Putfield add constraint " + p + " " + pVal;
                System.err.println(S);
              }
              sideEffect.b |= system.newFieldWrite(p, assign, pVal, object);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
  }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    /**
     * Subclasses can override as needed
     */
    protected IntSet filterInstances(IntSet value) {
      return value;
    }

    @Override
    public int hashCode() {
      return 9857 * getField().hashCode() + getFixedSet().hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o != null && o.getClass().equals(getClass())) {
        PutFieldOperator other = (PutFieldOperator) o;
        return getField().equals(other.getField()) && getFixedSet().equals(other.getFixedSet());
      } else {
        return false;
      }
    }

    /**
     * subclasses (e.g. XTA) can override this to enforce a filtered assignment. returns null if there's a problem.
     */
    public UnaryOperator getPutAssignmentOperator() {
      return assignOperator;
    }

    /**
     * @return Returns the field.
     */
    protected IField getField() {
      return field;
    }

    @Override
    protected boolean isLoadOperator() {
      return false;
    }
  }

  /**
   * Update the points-to-set for a field to include a particular instance key.
   */
  public final class InstancePutFieldOperator extends UnaryOperator implements IPointerOperator {
    final private IField field;

    final private InstanceKey instance;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "InstancePutField" + field;
    }

    public InstancePutFieldOperator(IField field, InstanceKey instance) {
      this.field = field;
      this.instance = instance;
    }

    /**
     * Simply add the instance to each relevant points-to set.
     */
    @Override
    public byte evaluate(PointsToSetVariable dummyLHS, PointsToSetVariable var) {
      PointsToSetVariable ref = var;
      if (ref.size() == 0) {
        return NOT_CHANGED;
      }
      IntSet value = ref.getValue();
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!representsNullType(I)) {
            PointerKey p = getPointerKeyForInstanceField(I, field);
            if (p != null) {
              sideEffect.b |= system.newConstraint(p, instance);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    @Override
    public int hashCode() {
      return field.hashCode() + 9839 * instance.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof InstancePutFieldOperator) {

        InstancePutFieldOperator other = (InstancePutFieldOperator) o;
        return field.equals(other.field) && instance.equals(other.instance);
      } else {
        return false;
      }
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  /**
   * Update the points-to-set for an array contents to include a particular instance key.
   */
  public final class InstanceArrayStoreOperator extends UnaryOperator implements IPointerOperator {
    final private InstanceKey instance;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "InstanceArrayStore ";
    }

    public InstanceArrayStoreOperator(InstanceKey instance) {
      this.instance = instance;
    }

    /**
     * Simply add the instance to each relevant points-to set.
     */
    @Override
    public byte evaluate(PointsToSetVariable dummyLHS, PointsToSetVariable var) {
      PointsToSetVariable arrayref = var;
      if (arrayref.size() == 0) {
        return NOT_CHANGED;
      }
      IntSet value = arrayref.getValue();
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!I.getConcreteType().isArrayClass()) {
            return;
          }
          if (I instanceof ZeroLengthArrayInNode) {
            return;
          }
          TypeReference C = I.getConcreteType().getReference().getArrayElementType();
          if (C.isPrimitiveType()) {
            return;
          }
          IClass contents = getClassHierarchy().lookupClass(C);
          if (contents == null) {
            assert false : "null type for " + C + " " + I.getConcreteType();
          }
          PointerKey p = getPointerKeyForArrayContents(I);
          if (contents.isInterface()) {
            if (getClassHierarchy().implementsInterface(instance.getConcreteType(), contents)) {
              sideEffect.b |= system.newConstraint(p, instance);
            }
          } else {
            if (getClassHierarchy().isSubclassOf(instance.getConcreteType(), contents)) {
              sideEffect.b |= system.newConstraint(p, instance);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    @Override
    public int hashCode() {
      return 9839 * instance.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof InstanceArrayStoreOperator) {
        InstanceArrayStoreOperator other = (InstanceArrayStoreOperator) o;
        return instance.equals(other.instance);
      } else {
        return false;
      }
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  protected MutableIntSet getMutableInstanceKeysForClass(IClass klass) {
    return system.cloneInstanceKeysForClass(klass);
  }

  protected IntSet getInstanceKeysForClass(IClass klass) {
    return system.getInstanceKeysForClass(klass);
  }

  /**
   * @param klass a class
   * @return an int set which represents the subset of S that correspond to subtypes of klass
   */
  protected IntSet filterForClass(IntSet S, IClass klass) {
    MutableIntSet filter = null;
    if (klass.getReference().equals(TypeReference.JavaLangObject)) {
      return S;
    } else {
      filter = getMutableInstanceKeysForClass(klass);

      boolean debug = false;
      if (DEBUG_FILTER) {
        String s = "klass     " + klass;
        System.err.println(s);
        System.err.println("initial filter    " + filter);
      }
      filter.intersectWith(S);

      if (DEBUG_FILTER && debug) {
        System.err.println("final filter    " + filter);
      }
    }
    return filter;
  }

  protected class InverseFilterOperator extends FilterOperator {
    public InverseFilterOperator() {
      super();
    }

    @Override
    public String toString() {
      return "InverseFilter";
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    @Override
    public boolean isComplex() {
      return false;
    }

    /*
     * simply check if rhs contains a malleable.
     * 
     * @see com.ibm.wala.dataflow.UnaryOperator#evaluate(com.ibm.wala.dataflow.IVariable, com.ibm.wala.dataflow.IVariable)
     */
    @Override
    public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable rhs) {

      FilteredPointerKey pk = (FilteredPointerKey) lhs.getPointerKey();
      FilteredPointerKey.TypeFilter filter = pk.getTypeFilter();

      boolean debug = false;
      if (DEBUG_FILTER) {
        String S = "EVAL InverseFilter/" + filter + " " + lhs.getPointerKey() + " " + rhs.getPointerKey();
        S += "\nEVAL      " + lhs + " " + rhs;
        System.err.println(S);
      }
      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }

      boolean changed = filter.addInverseFiltered(system, lhs, rhs);

      if (DEBUG_FILTER) {
        if (debug) {
          System.err.println("RESULT " + lhs + (changed ? " (changed)" : ""));
        }
      }
      return changed ? CHANGED : NOT_CHANGED;
    }
  }

  protected IPointsToSolver getSolver() {
    return solver;
  }

  /**
   * Add constraints when the interpretation of a node changes (e.g. reflection)
   * @param monitor 
   * @throws CancelException 
   */
  public void addConstraintsFromChangedNode(CGNode node, IProgressMonitor monitor) throws CancelException {
    unconditionallyAddConstraintsFromNode(node, monitor);
  }

  protected abstract boolean unconditionallyAddConstraintsFromNode(CGNode node, IProgressMonitor monitor) throws CancelException;

  protected static class MutableBoolean {
    // a horrendous hack since we don't have closures
    boolean b = false;
  }

  public AnalysisCache getAnalysisCache() {
    return analysisCache;
  };

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
  final protected IClassHierarchy cha;

  /**
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.propagation;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.ibm.wala.analysis.reflection.IllegalArgumentExceptionContext;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.SyntheticClass;
import com.ibm.wala.fixpoint.UnaryOperator;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.CallGraphBuilder;
import com.ibm.wala.ipa.callgraph.CallGraphBuilderCancelException;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.CancelRuntimeException;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetAction;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * This abstract base class provides the general algorithm for a call graph builder that relies on propagation through an iterative
 * dataflow solver
 * 
 * TODO: This implementation currently keeps all points to sets live ... even those for local variables that do not span
 * interprocedural boundaries. This may be too space-inefficient .. we can consider recomputing local sets on demand.
 */
public abstract class PropagationCallGraphBuilder implements CallGraphBuilder {
  private final static boolean DEBUG_ALL = false;

  final static boolean DEBUG_ASSIGN = DEBUG_ALL | false;

  private final static boolean DEBUG_ARRAY_LOAD = DEBUG_ALL | false;

  private final static boolean DEBUG_ARRAY_STORE = DEBUG_ALL | false;

  private final static boolean DEBUG_FILTER = DEBUG_ALL | false;

  final protected static boolean DEBUG_GENERAL = DEBUG_ALL | false;

  private final static boolean DEBUG_GET = DEBUG_ALL | false;

  private final static boolean DEBUG_PUT = DEBUG_ALL | false;

  private final static boolean DEBUG_ENTRYPOINTS = DEBUG_ALL | false;

  /**
   * Meta-data regarding how pointers are modeled
   */
  protected PointerKeyFactory pointerKeyFactory;

  /**
   * The object that represents the java.lang.Object class
   */
  final private IClass JAVA_LANG_OBJECT;

  /**
   * Governing class hierarchy
   */
   * Special rules for bypassing Java calls
   */
  final protected AnalysisOptions options;

  /**
   * Cache of IRs and things
   */
  private final AnalysisCache analysisCache;

  /**
   * Set of nodes that have already been traversed for constraints
   */
  final private Set alreadyVisited = HashSetFactory.make();

  /**
   * At any given time, the set of nodes that have been discovered but not yet processed for constraints
   */
  private Set discoveredNodes = HashSetFactory.make();

  /**
   * Set of calls (CallSiteReferences) that are created by entrypoints
   */
  final protected Set entrypointCallSites = HashSetFactory.make();

  /**
   * The system of constraints used to build this graph
   */
  protected PropagationSystem system;

  /**
   * Algorithm used to solve the system of constraints
   */
  private IPointsToSolver solver;

  /**
   * The call graph under construction
   */
  protected final ExplicitCallGraph callGraph;

  /**
   * Singleton operator for assignments
   */
  protected final static AssignOperator assignOperator = new AssignOperator();

  /**
   * singleton operator for filter
   */
  public final FilterOperator filterOperator = new FilterOperator();

  /**
   * singleton operator for inverse filter
   */
  protected final InverseFilterOperator inverseFilterOperator = new InverseFilterOperator();

  /**
   * An object which interprets methods in context
   */
  private SSAContextInterpreter contextInterpreter;

  /**
   * A context selector which may use information derived from the propagation-based dataflow.
   */
  protected ContextSelector contextSelector;

  /**
   * An object that abstracts how to model instances in the heap.
   */
  protected InstanceKeyFactory instanceKeyFactory;

  /**
   * Algorithmic choice: should the GetfieldOperator and PutfieldOperator cache its previous history to reduce work?
   */
  final private boolean rememberGetPutHistory = true;

  /**
   * @param cha governing class hierarchy
   * @param options governing call graph construction options
   * @param pointerKeyFactory factory which embodies pointer abstraction policy
   */
  protected PropagationCallGraphBuilder(IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache,
      PointerKeyFactory pointerKeyFactory) {
    if (cha == null) {
      throw new IllegalArgumentException("cha is null");
    }
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    assert cache != null;
    this.cha = cha;
    this.options = options;
    this.analysisCache = cache;
    // we need pointer keys to handle reflection
    assert pointerKeyFactory != null;
    this.pointerKeyFactory = pointerKeyFactory;
    callGraph = createEmptyCallGraph(cha, options);
    try {
      callGraph.init();
    } catch (CancelException e) {
      if (DEBUG_GENERAL) {
        System.err.println("Could not initialize the call graph due to node number constraints: " + e.getMessage());
      }
    }
    callGraph.setInterpreter(contextInterpreter);
    JAVA_LANG_OBJECT = cha.lookupClass(TypeReference.JavaLangObject);
  }

  protected ExplicitCallGraph createEmptyCallGraph(IClassHierarchy cha, AnalysisOptions options) {
    return new ExplicitCallGraph(cha, options, getAnalysisCache());
  }

  /**
   * @return true iff the klass represents java.lang.Object
   */
  protected boolean isJavaLangObject(IClass klass) {
    return (klass.getReference().equals(TypeReference.JavaLangObject));
  }

  public CallGraph makeCallGraph(AnalysisOptions options) throws IllegalArgumentException, CancelException {
    return makeCallGraph(options, null);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.CallGraphBuilder#makeCallGraph(com.ibm.wala.ipa.callgraph.AnalysisOptions)
   */
  public CallGraph makeCallGraph(AnalysisOptions options, IProgressMonitor monitor) throws IllegalArgumentException,
      CallGraphBuilderCancelException {
    if (options == null) {
      throw new IllegalArgumentException("options is null");
    }
    system = makeSystem(options);

    if (DEBUG_GENERAL) {
      System.err.println("Enter makeCallGraph!");
    }

    if (DEBUG_GENERAL) {
      System.err.println("Initialized call graph");
    }

    system.setMinEquationsForTopSort(options.getMinEquationsForTopSort());
    system.setTopologicalGrowthFactor(options.getTopologicalGrowthFactor());
    system.setMaxEvalBetweenTopo(options.getMaxEvalBetweenTopo());

    discoveredNodes = HashSetFactory.make();
    discoveredNodes.add(callGraph.getFakeRootNode());

    // Set up the initially reachable methods and classes
    for (Iterator it = options.getEntrypoints().iterator(); it.hasNext();) {
      Entrypoint E = (Entrypoint) it.next();
      if (DEBUG_ENTRYPOINTS) {
        System.err.println("Entrypoint: " + E);
      }
      SSAAbstractInvokeInstruction call = E.addCall((AbstractRootMethod) callGraph.getFakeRootNode().getMethod());

      if (call == null) {
        Warnings.add(EntrypointResolutionWarning.create(E));
      } else {
        entrypointCallSites.add(call.getCallSite());
      }
    }
    
/** BEGIN Custom change: throw exception on empty entry points. This is a severe issue that should not go undetected! */
    if (entrypointCallSites.isEmpty()) {
      throw new IllegalStateException("Could not create a entrypoint callsites."
          + " This happens when some parameters of the method can not be generated automatically "
          + "(e.g. when they refer to an interface or an abstract class).");
    }
    
/** END Custom change: throw exception on empty entry points. This is a severe issue that should not go undetected! */
    customInit();

    solver = makeSolver();
    try {
      solver.solve(monitor);
    } catch (CancelException e) {
      CallGraphBuilderCancelException c = CallGraphBuilderCancelException.createCallGraphBuilderCancelException(e, callGraph,
          system.extractPointerAnalysis(this));
      throw c;
    } catch (CancelRuntimeException e) {
      CallGraphBuilderCancelException c = CallGraphBuilderCancelException.createCallGraphBuilderCancelException(e, callGraph,
          system.extractPointerAnalysis(this));
      throw c;
    }

    return callGraph;
  }

  protected PropagationSystem makeSystem(AnalysisOptions options) {
    return new PropagationSystem(callGraph, pointerKeyFactory, instanceKeyFactory);
  }

  protected abstract IPointsToSolver makeSolver();

  /**
   * A warning for when we fail to resolve a call to an entrypoint
   */
  private static class EntrypointResolutionWarning extends Warning {

    final Entrypoint entrypoint;

    EntrypointResolutionWarning(Entrypoint entrypoint) {
      super(Warning.SEVERE);
      this.entrypoint = entrypoint;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + entrypoint;
    }

    public static EntrypointResolutionWarning create(Entrypoint entrypoint) {
      return new EntrypointResolutionWarning(entrypoint);
    }
  }

  protected void customInit() {
  }

  /**
   * Add constraints for a node.
   * @param monitor 
   * 
   * @return true iff any new constraints are added.
   */
  protected abstract boolean addConstraintsFromNode(CGNode n, IProgressMonitor monitor) throws CancelException;

  /**
   * Add constraints from newly discovered nodes. Note: the act of adding constraints may discover new nodes, so this routine is
   * iterative.
   * 
   * @return true iff any new constraints are added.
   * @throws CancelException 
   */
  protected boolean addConstraintsFromNewNodes(IProgressMonitor monitor) throws CancelException {
    boolean result = false;
    while (!discoveredNodes.isEmpty()) {
      Iterator it = discoveredNodes.iterator();
      discoveredNodes = HashSetFactory.make();
      while (it.hasNext()) {
        CGNode n = it.next();
        result |= addConstraintsFromNode(n, monitor);
      }
    }
    return result;
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the local variable identified by
   *         the value number parameter.
   */
  public PointerKey getPointerKeyForLocal(CGNode node, int valueNumber) {
    return pointerKeyFactory.getPointerKeyForLocal(node, valueNumber);
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the local variable identified by
   *         the value number parameter.
   */
  public FilteredPointerKey getFilteredPointerKeyForLocal(CGNode node, int valueNumber, FilteredPointerKey.TypeFilter filter) {
    assert filter != null;
    return pointerKeyFactory.getFilteredPointerKeyForLocal(node, valueNumber, filter);
  }

  public FilteredPointerKey getFilteredPointerKeyForLocal(CGNode node, int valueNumber, IClass filter) {
    return getFilteredPointerKeyForLocal(node, valueNumber, new FilteredPointerKey.SingleClassFilter(filter));
  }

  public FilteredPointerKey getFilteredPointerKeyForLocal(CGNode node, int valueNumber, InstanceKey filter) {
    return getFilteredPointerKeyForLocal(node, valueNumber, new FilteredPointerKey.SingleInstanceFilter(filter));
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the return value for a node
   */
  public PointerKey getPointerKeyForReturnValue(CGNode node) {
    return pointerKeyFactory.getPointerKeyForReturnValue(node);
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the exceptional return value
   */
  public PointerKey getPointerKeyForExceptionalReturnValue(CGNode node) {
    return pointerKeyFactory.getPointerKeyForExceptionalReturnValue(node);
  }

  /**
   * @return the PointerKey that acts as a representative for the class of pointers that includes the contents of the static field
   */
  public PointerKey getPointerKeyForStaticField(IField f) {
    assert f != null : "null FieldReference";
    return pointerKeyFactory.getPointerKeyForStaticField(f);
  }

  /**
   * @return the PointerKey that acts as a representation for the class of pointers that includes the given instance field. null if
   *         there's some problem.
   * @throws IllegalArgumentException if I is null
   * @throws IllegalArgumentException if field is null
   */
  public PointerKey getPointerKeyForInstanceField(InstanceKey I, IField field) {
    if (field == null) {
      throw new IllegalArgumentException("field is null");
    }
    if (I == null) {
      throw new IllegalArgumentException("I is null");
    }
    IClass t = field.getDeclaringClass();
    IClass C = I.getConcreteType();
    if (!(C instanceof SyntheticClass)) {
      if (!getClassHierarchy().isSubclassOf(C, t)) {
        return null;
      }
    }

    return pointerKeyFactory.getPointerKeyForInstanceField(I, field);
  }

  /**
   * TODO: expand this API to differentiate between different array indices
   * 
   * @param I an InstanceKey representing an abstract array
   * @return the PointerKey that acts as a representation for the class of pointers that includes the given array contents, or null
   *         if none found.
   * @throws IllegalArgumentException if I is null
   */
  public PointerKey getPointerKeyForArrayContents(InstanceKey I) {
    if (I == null) {
      throw new IllegalArgumentException("I is null");
    }
    IClass C = I.getConcreteType();
    if (!C.isArrayClass()) {
      assert false : "illegal arguments: " + I;
    }
    return pointerKeyFactory.getPointerKeyForArrayContents(I);
  }

  /**
   * Handle assign of a particular exception instance into an exception variable
   * 
   * @param exceptionVar points-to set for a variable representing a caught exception
   * @param catchClasses set of TypeReferences that the exceptionVar may catch
   * @param e a particular exception instance
   */
  protected void assignInstanceToCatch(PointerKey exceptionVar, Set catchClasses, InstanceKey e) {
    if (catches(catchClasses, e.getConcreteType(), cha)) {
      system.newConstraint(exceptionVar, e);
    }
  }

  /**
   * Generate a set of constraints to represent assignment to an exception variable in a catch clause. Note that we use
   * FilterOperator to filter out types that the exception handler doesn't catch.
   * 
   * @param exceptionVar points-to set for a variable representing a caught exception
   * @param catchClasses set of TypeReferences that the exceptionVar may catch
   * @param e points-to-set representing a thrown exception that might be caught.
   */
  protected void addAssignmentsForCatchPointerKey(PointerKey exceptionVar, Set catchClasses, PointerKey e) {
    if (DEBUG_GENERAL) {
      System.err.println("addAssignmentsForCatch: " + catchClasses);
    }
    // this is tricky ... we want to filter based on a number of classes ... so we can't
    // just used a FilteredPointerKey for the exceptionVar. Instead, we create a new
    // "typed local" for each catch class, and coalesce the results using
    // assignment
    for (IClass c : catchClasses) {
      if (c.getReference().equals(c.getClassLoader().getLanguage().getThrowableType())) {
        system.newConstraint(exceptionVar, assignOperator, e);
      } else {
        FilteredPointerKey typedException = TypedPointerKey.make(exceptionVar, c);
        system.newConstraint(typedException, filterOperator, e);
        system.newConstraint(exceptionVar, assignOperator, typedException);
      }
    }
  }

  /**
  /**
   * A warning for when we fail to resolve a call to an entrypoint
   */
  @SuppressWarnings("unused")
  private static class ExceptionLookupFailure extends Warning {

    final TypeReference t;

    ExceptionLookupFailure(TypeReference t) {
      super(Warning.SEVERE);
      this.t = t;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + t;
    }

    public static ExceptionLookupFailure create(TypeReference t) {
      return new ExceptionLookupFailure(t);
    }
  }

  /**
   * A pointer key that delegates to an untyped variant, but adds a type filter
   */
  public final static class TypedPointerKey implements FilteredPointerKey {

    private final IClass type;

    private final PointerKey base;

    static TypedPointerKey make(PointerKey base, IClass type) {
      assert type != null;
      return new TypedPointerKey(base, type);
    }

    private TypedPointerKey(PointerKey base, IClass type) {
      this.type = type;
      this.base = base;
      assert type != null;
      assert !(type instanceof FilteredPointerKey);
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.FilteredPointerKey#getTypeFilter()
     */
    public TypeFilter getTypeFilter() {
      return new SingleClassFilter(type);
    }

    @Override
    public boolean equals(Object obj) {
      // instanceof is OK because this class is final
      if (obj instanceof TypedPointerKey) {
        TypedPointerKey other = (TypedPointerKey) obj;
        return type.equals(other.type) && base.equals(other.base);
      } else {
        return false;
      }
    }

    @Override
    public int hashCode() {
      return 67931 * base.hashCode() + type.hashCode();
    }

    @Override
    public String toString() {
      return "{ " + base + " type: " + type + "}";
    }

    public PointerKey getBase() {
      return base;
    }
  }

  /**
   * @param catchClasses Set of TypeReference
   * @param klass an Exception Class
   * @return true iff klass is a subclass of some element of the Set
   * @throws IllegalArgumentException if catchClasses is null
   */
  public static boolean catches(Set catchClasses, IClass klass, IClassHierarchy cha) {
    if (catchClasses == null) {
      throw new IllegalArgumentException("catchClasses is null");
    }
    // quick shortcut
    if (catchClasses.size() == 1) {
      IClass c = catchClasses.iterator().next();
      if (c != null && c.getReference().equals(TypeReference.JavaLangThread)) {
        return true;
      }
    }
    for (IClass c : catchClasses) {
      if (c != null && cha.isAssignableFrom(c, klass)) {
        return true;
      }
    }
    return false;
  }

  public static boolean representsNullType(InstanceKey key) throws IllegalArgumentException {
    if (key == null) {
      throw new IllegalArgumentException("key == null");
    }
    IClass cls = key.getConcreteType();
    Language L = cls.getClassLoader().getLanguage();
    return L.isNullType(cls.getReference());
  }
   * The FilterOperator is a filtered set-union. i.e. the LHS is `unioned' with the RHS, but filtered by the set associated with
   * this operator instance. The filter is the set of InstanceKeys corresponding to the target type of this cast. This is still
   * monotonic.
   * 
   * LHS U= (RHS n k)
   * 
   * 
   * Unary op: := Cast_k( )
   * 
   * (Again, technically a binary op -- see note for Assign)
   * 
   * TODO: these need to be canonicalized.
   * 
   */
  public class FilterOperator extends UnaryOperator implements IPointerOperator {

    protected FilterOperator() {
    }

    /*
     * @see com.ibm.wala.dataflow.UnaryOperator#evaluate(com.ibm.wala.dataflow.IVariable, com.ibm.wala.dataflow.IVariable)
     */
    @Override
    public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable rhs) {

      FilteredPointerKey pk = (FilteredPointerKey) lhs.getPointerKey();

      if (DEBUG_FILTER) {
        String S = "EVAL Filter " + lhs.getPointerKey() + " " + rhs.getPointerKey();
        S += "\nEVAL      " + lhs + " " + rhs;
        System.err.println(S);
      }
      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }

      boolean changed = false;
      FilteredPointerKey.TypeFilter filter = pk.getTypeFilter();
      changed = filter.addFiltered(system, lhs, rhs);

      if (DEBUG_FILTER) {
        System.err.println("RESULT " + lhs + (changed ? " (changed)" : ""));
      }

      return changed ? CHANGED : NOT_CHANGED;
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return false;
    }

    @Override
    public String toString() {
      return "Filter ";
    }

    @Override
    public boolean equals(Object obj) {
      // these objects are canonicalized for the duration of a solve
      return this == obj;
    }

    @Override
    public int hashCode() {
      return 88651;
    }

  }

  public IClassHierarchy getClassHierarchy() {
    return cha;
  }

  public AnalysisOptions getOptions() {
    return options;
  }

  public IClass getJavaLangObject() {
    return JAVA_LANG_OBJECT;
  }

  public ExplicitCallGraph getCallGraph() {
    return callGraph;
  }

  /**
   * Subclasses must register the context interpreter before building a call graph.
   */
  public void setContextInterpreter(SSAContextInterpreter interpreter) {
    contextInterpreter = interpreter;
    callGraph.setInterpreter(interpreter);
  }

  /*
   * @see com.ibm.detox.ipa.callgraph.CallGraphBuilder#getPointerAnalysis()
   */
  public PointerAnalysis getPointerAnalysis() {
    return system.extractPointerAnalysis(this);
  }

  public PropagationSystem getPropagationSystem() {
    return system;
  }

  public PointerKeyFactory getPointerKeyFactory() {
    return pointerKeyFactory;
  }

/** BEGIN Custom change: setter for pointerkey factory */
  public void setPointerKeyFactory(PointerKeyFactory pkFact) {
    pointerKeyFactory = pkFact;
  }

/** END Custom change: setter for pointerkey factory */
  public RTAContextInterpreter getContextInterpreter() {
    return contextInterpreter;
  }

  /**
   * @param caller the caller node
   * @param iKey an abstraction of the receiver of the call (or null if not applicable)
   * @return the CGNode to which this particular call should dispatch.
   */
  protected CGNode getTargetForCall(CGNode caller, CallSiteReference site, IClass recv, InstanceKey iKey[]) {

    IMethod targetMethod = options.getMethodTargetSelector().getCalleeTarget(caller, site, recv);

    // this most likely indicates an exclusion at work; the target selector
    // should have issued a warning
    if (targetMethod == null || targetMethod.isAbstract()) {
      return null;
    }
    Context targetContext = contextSelector.getCalleeTarget(caller, site, targetMethod, iKey);
    
    if (targetContext instanceof IllegalArgumentExceptionContext) {
      return null;
    }
    try {
      return getCallGraph().findOrCreateNode(targetMethod, targetContext);
    } catch (CancelException e) {
      return null;
    }
  }
  
  /**
   * @return the context selector for this call graph builder
   */
  public ContextSelector getContextSelector() {
    return contextSelector;
  }

  public void setContextSelector(ContextSelector selector) {
    contextSelector = selector;
  }

  public InstanceKeyFactory getInstanceKeys() {
    return instanceKeyFactory;
  }

  public void setInstanceKeys(InstanceKeyFactory keys) {
    this.instanceKeyFactory = keys;
  }

  /**
   * @return the InstanceKey that acts as a representative for the class of objects that includes objects allocated at the given new
   *         instruction in the given node
   */
  public InstanceKey getInstanceKeyForAllocation(CGNode node, NewSiteReference allocation) {
    return instanceKeyFactory.getInstanceKeyForAllocation(node, allocation);
  }

  /**
   * @param dim the dimension of the array whose instance we would like to model. dim == 0 represents the first dimension, e.g., the
   *          [Object; instances in [[Object; e.g., the [[Object; instances in [[[Object; dim == 1 represents the second dimension,
   *          e.g., the [Object instances in [[[Object;
   * @return the InstanceKey that acts as a representative for the class of array contents objects that includes objects allocated
   *         at the given new instruction in the given node
   */
  public InstanceKey getInstanceKeyForMultiNewArray(CGNode node, NewSiteReference allocation, int dim) {
    return instanceKeyFactory.getInstanceKeyForMultiNewArray(node, allocation, dim);
  }

  public  InstanceKey getInstanceKeyForConstant(TypeReference type, T S) {
    return instanceKeyFactory.getInstanceKeyForConstant(type, S);
  }

  public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
    return instanceKeyFactory.getInstanceKeyForClassObject(type);
  }

  public boolean haveAlreadyVisited(CGNode node) {
    return alreadyVisited.contains(node);
  }

  protected void markAlreadyVisited(CGNode node) {
    alreadyVisited.add(node);
  }

  /**
   * record that we've discovered a node
   */
  public void markDiscovered(CGNode node) {
    discoveredNodes.add(node);
  }

  protected void markChanged(CGNode node) {
    alreadyVisited.remove(node);
    discoveredNodes.add(node);
  }

  protected boolean wasChanged(CGNode node) {
    return discoveredNodes.contains(node) && !alreadyVisited.contains(node);
  }

  /**
   * Binary op: := ArrayLoad( <arrayref>) Side effect: Creates new equations.
   */
  public final class ArrayLoadOperator extends UnarySideEffect implements IPointerOperator {
    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "ArrayLoad";
    }

    public ArrayLoadOperator(PointsToSetVariable def) {
      super(def);
      system.registerFixedSet(def, this);
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_ARRAY_LOAD) {
        PointsToSetVariable def = getFixedSet();
        String S = "EVAL ArrayLoad " + rhs.getPointerKey() + " " + def.getPointerKey();
        System.err.println(S);
        System.err.println("EVAL ArrayLoad " + def + " " + rhs);
        if (priorInstances != null) {
          System.err.println("prior instances: " + priorInstances + " " + priorInstances.getClass());
        }
      }

      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }
      final PointerKey object = rhs.getPointerKey();

      PointsToSetVariable def = getFixedSet();
      final PointerKey dVal = def.getPointerKey();

      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!I.getConcreteType().isArrayClass()) {
            return;
          }
          TypeReference C = I.getConcreteType().getReference().getArrayElementType();
          if (C.isPrimitiveType()) {
            return;
          }
          PointerKey p = getPointerKeyForArrayContents(I);
          if (p == null) {
            return;
          }

          if (DEBUG_ARRAY_LOAD) {
            System.err.println("ArrayLoad add assign: " + dVal + " " + p);
          }
          sideEffect.b |= system.newFieldRead(dVal, assignOperator, p, object);
        }
      };
      if (priorInstances != null) {
        rhs.getValue().foreachExcluding(priorInstances, action);
        priorInstances.addAll(rhs.getValue());
      } else {
        rhs.getValue().foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    @Override
    public int hashCode() {
      return 9871 + super.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      return super.equals(o);
    }

    @Override
    protected boolean isLoadOperator() {
      return true;
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  /**
   * Binary op: := ArrayStore( <arrayref>) Side effect: Creates new equations.
   */
  public final class ArrayStoreOperator extends UnarySideEffect implements IPointerOperator {
    @Override
    public String toString() {
      return "ArrayStore";
    }

    public ArrayStoreOperator(PointsToSetVariable val) {
      super(val);
      system.registerFixedSet(val, this);
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_ARRAY_STORE) {
        PointsToSetVariable val = getFixedSet();
        String S = "EVAL ArrayStore " + rhs.getPointerKey() + " " + val.getPointerKey();
        System.err.println(S);
        System.err.println("EVAL ArrayStore " + rhs + " " + getFixedSet());
      }

      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }
      PointerKey object = rhs.getPointerKey();

      PointsToSetVariable val = getFixedSet();
      PointerKey pVal = val.getPointerKey();

      List instances = system.getInstances(rhs.getValue());
      boolean sideEffect = false;
      for (Iterator it = instances.iterator(); it.hasNext();) {
        InstanceKey I = it.next();
        if (!I.getConcreteType().isArrayClass()) {
          continue;
        }
        if (I instanceof ZeroLengthArrayInNode) {
          continue;
        }
        TypeReference C = I.getConcreteType().getReference().getArrayElementType();
        if (C.isPrimitiveType()) {
          continue;
        }
        IClass contents = getClassHierarchy().lookupClass(C);
        if (contents == null) {
          assert false : "null type for " + C + " " + I.getConcreteType();
        }
        PointerKey p = getPointerKeyForArrayContents(I);
        if (DEBUG_ARRAY_STORE) {
          System.err.println("ArrayStore add filtered-assign: " + p + " " + pVal);
        }

        // note that the following is idempotent
        if (isJavaLangObject(contents)) {
          sideEffect |= system.newFieldWrite(p, assignOperator, pVal, object);
        } else {
          sideEffect |= system.newFieldWrite(p, filterOperator, pVal, object);
        }
      }
      byte sideEffectMask = sideEffect ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    @Override
    public int hashCode() {
      return 9859 + super.hashCode();
    }

    public boolean isComplex() {
      return true;
    }

    @Override
    public boolean equals(Object o) {
      return super.equals(o);
    }

    @Override
    protected boolean isLoadOperator() {
      return false;
    }
  }

  /**
   * Binary op: := GetField( ) Side effect: Creates new equations.
   */
  public class GetFieldOperator extends UnarySideEffect implements IPointerOperator {
    private final IField field;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    public GetFieldOperator(IField field, PointsToSetVariable def) {
      super(def);
      this.field = field;
      system.registerFixedSet(def, this);
    }

    @Override
    public String toString() {
      return "GetField " + getField() + "," + getFixedSet().getPointerKey();
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_GET) {
        String S = "EVAL GetField " + getField() + " " + getFixedSet().getPointerKey() + " " + rhs.getPointerKey() + getFixedSet()
            + " " + rhs;
        System.err.println(S);
      }

      PointsToSetVariable ref = rhs;
      if (ref.size() == 0) {
        return NOT_CHANGED;
      }
      final PointerKey object = ref.getPointerKey();
      PointsToSetVariable def = getFixedSet();
      final PointerKey dVal = def.getPointerKey();

      IntSet value = filterInstances(ref.getValue());
      if (DEBUG_GET) {
        System.err.println("filtered value: " + value + " " + value.getClass());
        if (priorInstances != null) {
          System.err.println("prior instances: " + priorInstances + " " + priorInstances.getClass());
        }
      }
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!representsNullType(I)) {
            PointerKey p = getPointerKeyForInstanceField(I, getField());

            if (p != null) {
              if (DEBUG_GET) {
                String S = "Getfield add constraint " + dVal + " " + p;
                System.err.println(S);
              }
              sideEffect.b |= system.newFieldRead(dVal, assignOperator, p, object);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    /**
     * Subclasses can override as needed
     */
    protected IntSet filterInstances(IntSet value) {
      return value;
    }

    @Override
    public int hashCode() {
      return 9857 * getField().hashCode() + getFixedSet().hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof GetFieldOperator) {
        GetFieldOperator other = (GetFieldOperator) o;
        return getField().equals(other.getField()) && getFixedSet().equals(other.getFixedSet());
      } else {
        return false;
      }
    }

    protected IField getField() {
      return field;
    }

    @Override
    protected boolean isLoadOperator() {
      return true;
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  /**
   * Operator that represents a putfield
   */
  public class PutFieldOperator extends UnarySideEffect implements IPointerOperator {
    private final IField field;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "PutField" + getField();
    }

    public PutFieldOperator(IField field, PointsToSetVariable val) {
      super(val);
      this.field = field;
      system.registerFixedSet(val, this);
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }

    @Override
    public byte evaluate(PointsToSetVariable rhs) {
      if (DEBUG_PUT) {
        String S = "EVAL PutField " + getField() + " " + (getFixedSet()).getPointerKey() + " " + rhs.getPointerKey()
            + getFixedSet() + " " + rhs;
        System.err.println(S);
      }

      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }
      final PointerKey object = rhs.getPointerKey();

      PointsToSetVariable val = getFixedSet();
      final PointerKey pVal = val.getPointerKey();
      IntSet value = rhs.getValue();
      value = filterInstances(value);
      final UnaryOperator assign = getPutAssignmentOperator();
      if (assign == null) {
        Assertions.UNREACHABLE();
      }
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!representsNullType(I)) {
            if (DEBUG_PUT) {
              String S = "Putfield consider instance " + I;
              System.err.println(S);
            }
            PointerKey p = getPointerKeyForInstanceField(I, getField());
            if (p != null) {
              if (DEBUG_PUT) {
                String S = "Putfield add constraint " + p + " " + pVal;
                System.err.println(S);
              }
              sideEffect.b |= system.newFieldWrite(p, assign, pVal, object);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    /**
     * Subclasses can override as needed
     */
    protected IntSet filterInstances(IntSet value) {
      return value;
    }

    @Override
    public int hashCode() {
      return 9857 * getField().hashCode() + getFixedSet().hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o != null && o.getClass().equals(getClass())) {
        PutFieldOperator other = (PutFieldOperator) o;
        return getField().equals(other.getField()) && getFixedSet().equals(other.getFixedSet());
      } else {
        return false;
      }
    }

    /**
     * subclasses (e.g. XTA) can override this to enforce a filtered assignment. returns null if there's a problem.
     */
    public UnaryOperator getPutAssignmentOperator() {
      return assignOperator;
    }

    /**
     * @return Returns the field.
     */
    protected IField getField() {
      return field;
    }

    @Override
    protected boolean isLoadOperator() {
      return false;
    }
  }

  /**
   * Update the points-to-set for a field to include a particular instance key.
   */
  public final class InstancePutFieldOperator extends UnaryOperator implements IPointerOperator {
    final private IField field;

    final private InstanceKey instance;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "InstancePutField" + field;
    }

    public InstancePutFieldOperator(IField field, InstanceKey instance) {
      this.field = field;
      this.instance = instance;
    }

    /**
     * Simply add the instance to each relevant points-to set.
     */
    @Override
    public byte evaluate(PointsToSetVariable dummyLHS, PointsToSetVariable var) {
      PointsToSetVariable ref = var;
      if (ref.size() == 0) {
        return NOT_CHANGED;
      }
      IntSet value = ref.getValue();
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!representsNullType(I)) {
            PointerKey p = getPointerKeyForInstanceField(I, field);
            if (p != null) {
              sideEffect.b |= system.newConstraint(p, instance);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    @Override
    public int hashCode() {
      return field.hashCode() + 9839 * instance.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof InstancePutFieldOperator) {
        InstancePutFieldOperator other = (InstancePutFieldOperator) o;
        return field.equals(other.field) && instance.equals(other.instance);
      } else {
        return false;
      }
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  /**
   * Update the points-to-set for an array contents to include a particular instance key.
   */
  public final class InstanceArrayStoreOperator extends UnaryOperator implements IPointerOperator {
    final private InstanceKey instance;

    protected final MutableIntSet priorInstances = rememberGetPutHistory ? IntSetUtil.make() : null;

    @Override
    public String toString() {
      return "InstanceArrayStore ";
    }

    public InstanceArrayStoreOperator(InstanceKey instance) {
      this.instance = instance;
    }

    /**
     * Simply add the instance to each relevant points-to set.
     */
    @Override
    public byte evaluate(PointsToSetVariable dummyLHS, PointsToSetVariable var) {
      PointsToSetVariable arrayref = var;
      if (arrayref.size() == 0) {
        return NOT_CHANGED;
      }
      IntSet value = arrayref.getValue();
      final MutableBoolean sideEffect = new MutableBoolean();
      IntSetAction action = new IntSetAction() {
        public void act(int i) {
          InstanceKey I = system.getInstanceKey(i);
          if (!I.getConcreteType().isArrayClass()) {
            return;
          }
          if (I instanceof ZeroLengthArrayInNode) {
            return;
          }
          TypeReference C = I.getConcreteType().getReference().getArrayElementType();
          if (C.isPrimitiveType()) {
            return;
          }
          IClass contents = getClassHierarchy().lookupClass(C);
          if (contents == null) {
            assert false : "null type for " + C + " " + I.getConcreteType();
          }
          PointerKey p = getPointerKeyForArrayContents(I);
          if (contents.isInterface()) {
            if (getClassHierarchy().implementsInterface(instance.getConcreteType(), contents)) {
              sideEffect.b |= system.newConstraint(p, instance);
            }
          } else {
            if (getClassHierarchy().isSubclassOf(instance.getConcreteType(), contents)) {
              sideEffect.b |= system.newConstraint(p, instance);
            }
          }
        }
      };
      if (priorInstances != null) {
        value.foreachExcluding(priorInstances, action);
        priorInstances.addAll(value);
      } else {
        value.foreach(action);
      }
      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    @Override
    public int hashCode() {
      return 9839 * instance.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof InstanceArrayStoreOperator) {
        InstanceArrayStoreOperator other = (InstanceArrayStoreOperator) o;
        return instance.equals(other.instance);
      } else {
        return false;
      }
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  protected MutableIntSet getMutableInstanceKeysForClass(IClass klass) {
    return system.cloneInstanceKeysForClass(klass);
  }

  protected IntSet getInstanceKeysForClass(IClass klass) {
    return system.getInstanceKeysForClass(klass);
  }

  /**
   * @param klass a class
   * @return an int set which represents the subset of S that correspond to subtypes of klass
   */
  protected IntSet filterForClass(IntSet S, IClass klass) {
    MutableIntSet filter = null;
    if (klass.getReference().equals(TypeReference.JavaLangObject)) {
      return S;
    } else {
      filter = getMutableInstanceKeysForClass(klass);

      boolean debug = false;
      if (DEBUG_FILTER) {
        String s = "klass     " + klass;
        System.err.println(s);
        System.err.println("initial filter    " + filter);
      }
      filter.intersectWith(S);

      if (DEBUG_FILTER && debug) {
        System.err.println("final filter    " + filter);
      }
    }
    return filter;
  }

  protected class InverseFilterOperator extends FilterOperator {
    public InverseFilterOperator() {
      super();
    }

    @Override
    public String toString() {
      return "InverseFilter";
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    @Override
    public boolean isComplex() {
      return false;
    }

    /*
     * simply check if rhs contains a malleable.
     * 
     * @see com.ibm.wala.dataflow.UnaryOperator#evaluate(com.ibm.wala.dataflow.IVariable, com.ibm.wala.dataflow.IVariable)
     */
    @Override
    public byte evaluate(PointsToSetVariable lhs, PointsToSetVariable rhs) {

      FilteredPointerKey pk = (FilteredPointerKey) lhs.getPointerKey();
      FilteredPointerKey.TypeFilter filter = pk.getTypeFilter();

      boolean debug = false;
      if (DEBUG_FILTER) {
        String S = "EVAL InverseFilter/" + filter + " " + lhs.getPointerKey() + " " + rhs.getPointerKey();
        S += "\nEVAL      " + lhs + " " + rhs;
        System.err.println(S);
      }
      if (rhs.size() == 0) {
        return NOT_CHANGED;
      }

      boolean changed = filter.addInverseFiltered(system, lhs, rhs);

      if (DEBUG_FILTER) {
        if (debug) {
          System.err.println("RESULT " + lhs + (changed ? " (changed)" : ""));
        }
      }
      return changed ? CHANGED : NOT_CHANGED;
    }
  }

  protected IPointsToSolver getSolver() {
    return solver;
  }

  /**
   * Add constraints when the interpretation of a node changes (e.g. reflection)
   * @param monitor 
   * @throws CancelException 
   */
  public void addConstraintsFromChangedNode(CGNode node, IProgressMonitor monitor) throws CancelException {
    unconditionallyAddConstraintsFromNode(node, monitor);
  }

  protected abstract boolean unconditionallyAddConstraintsFromNode(CGNode node, IProgressMonitor monitor) throws CancelException;

  protected static class MutableBoolean {
    // a horrendous hack since we don't have closures
    boolean b = false;
  }

  public AnalysisCache getAnalysisCache() {
    return analysisCache;
  };

}
File
PropagationCallGraphBuilder.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
import com.ibm.wala.ssa.SSAAbstractThrowInstruction;
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.propagation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.ibm.wala.analysis.reflection.CloneInterpreter;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.ProgramCounter;
import com.ibm.wala.fixpoint.AbstractOperator;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.ContextKey;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.impl.FakeRootMethod;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.ConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
    /**
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSACFG;
import com.ibm.wala.ssa.SSACFG.BasicBlock;
import com.ibm.wala.ssa.SSACFG.ExceptionHandlerBasicBlock;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAInstanceofInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.CancelRuntimeException;
import com.ibm.wala.util.MonitorUtil;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.functions.VoidFunction;
import com.ibm.wala.util.intset.IntIterator;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetAction;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.ref.ReferenceCleanser;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * This abstract base class provides the general algorithm for a call graph builder that relies on propagation through an iterative
 * dataflow solver, and constraints generated by statements in SSA form.
 * 
 * TODO: This implementation currently keeps all points to sets live ... even those for local variables that do not span
 * interprocedural boundaries. This may be too space-inefficient .. we can consider recomputing local sets on demand.
 */
public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGraphBuilder implements HeapModel {
  private final static boolean DEBUG = false;

  private final static boolean DEBUG_MULTINEWARRAY = DEBUG | false;

  /**
   * Should we periodically clear out soft reference caches in an attempt to help the GC?
   */
  public final static boolean PERIODIC_WIPE_SOFT_CACHES = true;

  /**
   * Interval which defines the period to clear soft reference caches
   */
  public final static int WIPE_SOFT_CACHE_INTERVAL = 2500;

  /**
   * Counter for wiping soft caches
   */
  private static int wipeCount = 0;

  /**
   * use type inference to avoid unnecessary filter constraints?
   */
  // private final static boolean OPTIMIZE_WITH_TYPE_INFERENCE = true;
  /**
   * An optimization: if we can locally determine the final solution for a points-to set, then don't actually create the points-to
   * set, but instead short circuit by propagating the final solution to all such uses.
   * 
   * String constants are ALWAYS considered invariant, regardless of the value of this flag.
   * 
   * However, if this flag is set, then the solver is more aggressive identifying invariants.
   * 
   * Doesn't play well with pre-transitive solver; turning off for now.
   */
  private final static boolean SHORT_CIRCUIT_INVARIANT_SETS = true;

  /**
   * An optimization: if we can locally determine that a particular pointer p has exactly one use, then we don't actually create the
   * points-to-set for p, but instead short-circuit by propagating the final solution to the unique use.
   * 
   * Doesn't play well with pre-transitive solver; turning off for now.
   */
  protected final static boolean SHORT_CIRCUIT_SINGLE_USES = true;

  /**
   * Should we change calls to clone() to assignments?
   */
  private final boolean clone2Assign = false;

  /**
   * Cache for efficiency
   */
  private final static Selector cloneSelector = CloneInterpreter.CLONE.getSelector();

  /**
   * set of class whose clinits have already been processed
   */
  private final Set clinitVisited = HashSetFactory.make();

  private IProgressMonitor monitor;

  protected SSAPropagationCallGraphBuilder(IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache,
      PointerKeyFactory pointerKeyFactory) {
    super(cha, options, cache, pointerKeyFactory);
    // this.usePreTransitiveSolver = options.usePreTransitiveSolver();
  }

  public SSAContextInterpreter getCFAContextInterpreter() {
    return (SSAContextInterpreter) getContextInterpreter();
  }

  /**
   * @param node
   * @param x
   * @param type
   * @return the instance key that represents the exception of type _type_ thrown by a particular PEI.
   * @throws IllegalArgumentException if ikFactory is null
   */
  public static InstanceKey getInstanceKeyForPEI(CGNode node, ProgramCounter x, TypeReference type, InstanceKeyFactory ikFactory) {
    if (ikFactory == null) {
      throw new IllegalArgumentException("ikFactory is null");
    }
    return ikFactory.getInstanceKeyForPEI(node, x, type);
  }

  /**
   * Visit all instructions in a node, and add dataflow constraints induced by each statement in the SSA form.
   * @throws CancelException 
   * 
   * @see com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder#addConstraintsFromNode(com.ibm.wala.ipa.callgraph.CGNode)
   */
  @Override
  protected boolean addConstraintsFromNode(CGNode node, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    if (haveAlreadyVisited(node)) {
      return false;
    } else {
      markAlreadyVisited(node);
    }
    return unconditionallyAddConstraintsFromNode(node, monitor);
  }

  @Override
  protected boolean unconditionallyAddConstraintsFromNode(CGNode node, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    if (PERIODIC_WIPE_SOFT_CACHES) {
      wipeCount++;
      if (wipeCount >= WIPE_SOFT_CACHE_INTERVAL) {
        wipeCount = 0;
        ReferenceCleanser.clearSoftCaches();
      }
    }

    if (DEBUG) {
      System.err.println("\n\nAdd constraints from node " + node);
    }
    IR ir = getCFAContextInterpreter().getIR(node);
    if (DEBUG) {
      if (ir == null) {
        System.err.println("\n   No statements\n");
      } else {
        try {
          System.err.println(ir.toString());
        } catch (Error e) {
          e.printStackTrace();
        }
      }
    }

    if (ir == null) {
      return false;
    }

    addNodeInstructionConstraints(node, monitor);

    DefUse du = getCFAContextInterpreter().getDU(node);
    addNodePassthruExceptionConstraints(node, ir, du);
    // conservatively assume something changed
    return true;
  }

  /**
   * @return a visitor to examine instructions in the ir
   */
  protected ConstraintVisitor makeVisitor(CGNode node) {
        }
    return new ConstraintVisitor(this, node);
  }

  /**
   * Add pointer flow constraints based on instructions in a given node
   * @throws CancelException 
   */
  protected void addNodeInstructionConstraints(CGNode node, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    ConstraintVisitor v = makeVisitor(node);

    IR ir = v.ir;
    ControlFlowGraph cfg = ir.getControlFlowGraph();
    for (Iterator x = cfg.iterator(); x.hasNext();) {
      BasicBlock b = (BasicBlock) x.next();
      addBlockInstructionConstraints(node, cfg, b, v, monitor);
      if (wasChanged(node)) {
        return;
      }
    }
  }

  /**
   * Add constraints for a particular basic block.
   * @throws CancelException 
   */
  protected void addBlockInstructionConstraints(CGNode node, ControlFlowGraph cfg, BasicBlock b,
      ConstraintVisitor v, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    v.setBasicBlock(b);

    // visit each instruction in the basic block.
    for (Iterator it = b.iterator(); it.hasNext();) {
      MonitorUtil.throwExceptionIfCanceled(monitor);      
      SSAInstruction s = it.next();
      if (s != null) {
        s.visit(v);
        if (wasChanged(node)) {
          return;
        }
      }
    }

    addPhiConstraints(node, cfg, b, v);
  }

  private void addPhiConstraints(CGNode node, ControlFlowGraph cfg, BasicBlock b,
      ConstraintVisitor v) {
    // visit each phi instruction in each successor block
    for (Iterator sbs = cfg.getSuccNodes(b); sbs.hasNext();) {
      BasicBlock sb = (BasicBlock) sbs.next();
      if (!sb.hasPhi()) {
        continue;
      }
      int n = 0;
      for (Iterator back = cfg.getPredNodes(sb); back.hasNext(); n++) {
        if (back.next() == b) {
          break;
        }
      }
      assert n < cfg.getPredNodeCount(sb);
      for (Iterator phis = sb.iteratePhis(); phis.hasNext();) {
        SSAPhiInstruction phi = (SSAPhiInstruction) phis.next();
        if (phi == null) {
          continue;
        }
        PointerKey def = getPointerKeyForLocal(node, phi.getDef());
        if (hasNoInterestingUses(node, phi.getDef(), v.du)) {
          system.recordImplicitPointsToSet(def);
        } else {
          // the following test restricts the constraints to reachable
          // paths, according to verification constraints
          if (phi.getUse(n) > 0) {
            PointerKey use = getPointerKeyForLocal(node, phi.getUse(n));
            if (contentsAreInvariant(v.symbolTable, v.du, phi.getUse(n))) {
              system.recordImplicitPointsToSet(use);
              InstanceKey[] ik = getInvariantContents(v.symbolTable, v.du, node, phi.getUse(n), this);
              for (int i = 0; i < ik.length; i++) {
                system.newConstraint(def, ik[i]);
              }
            } else {
              system.newConstraint(def, assignOperator, use);
            }
          }
        }
      }
    }
  }

  /**
   * Add constraints to represent the flow of exceptions to the exceptional return value for this node
   * 
   * @param node
   * @param ir
   */
  protected void addNodePassthruExceptionConstraints(CGNode node, IR ir, DefUse du) {
    // add constraints relating to thrown exceptions that reach the exit block.
    protected final PropagationSystem system;
    List peis = getIncomingPEIs(ir, ir.getExitBlock());
    PointerKey exception = getPointerKeyForExceptionalReturnValue(node);

    TypeReference throwableType = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getThrowableType();
    IClass c = node.getClassHierarchy().lookupClass(throwableType);
    addExceptionDefConstraints(ir, du, node, peis, exception, Collections.singleton(c));
  }

  /**
   * Generate constraints which assign exception values into an exception pointer
   * 
   * @param node governing node
   * @param peis list of PEI instructions
   * @param exceptionVar PointerKey representing a pointer to an exception value
   * @param catchClasses the types "caught" by the exceptionVar
   */
  private void addExceptionDefConstraints(IR ir, DefUse du, CGNode node, List peis, PointerKey exceptionVar,
      Set catchClasses) {
    if (DEBUG) {
      System.err.println("Add exception def constraints for node " + node);
    }
    for (Iterator it = peis.iterator(); it.hasNext();) {
      ProgramCounter peiLoc = it.next();
      if (DEBUG) {
        System.err.println("peiLoc: " + peiLoc);
      }
      SSAInstruction pei = ir.getPEI(peiLoc);

      if (DEBUG) {
        System.err.println("Add exceptions from pei " + pei);
      }

      if (pei instanceof SSAAbstractInvokeInstruction) {
        SSAAbstractInvokeInstruction s = (SSAAbstractInvokeInstruction) pei;
        PointerKey e = getPointerKeyForLocal(node, s.getException());

        if (!SHORT_CIRCUIT_SINGLE_USES || !hasUniqueCatchBlock(s, ir)) {
          addAssignmentsForCatchPointerKey(exceptionVar, catchClasses, e);
        }// else {
        // System.err.println("SKIPPING ASSIGNMENTS TO " + exceptionVar + " FROM " +
        // e);
        // }
      } else if (pei instanceof SSAAbstractThrowInstruction) {
        SSAAbstractThrowInstruction s = (SSAAbstractThrowInstruction) pei;
        PointerKey e = getPointerKeyForLocal(node, s.getException());

        if (contentsAreInvariant(ir.getSymbolTable(), du, s.getException())) {
          InstanceKey[] ik = getInvariantContents(ir.getSymbolTable(), du, node, s.getException(), this);
          for (int i = 0; i < ik.length; i++) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            assignInstanceToCatch(exceptionVar, catchClasses, ik[i]);
          }
        } else {
          addAssignmentsForCatchPointerKey(exceptionVar, catchClasses, e);
        }
      }

      // Account for those exceptions for which we do not actually have a
      // points-to set for
      // the pei, but just instance keys
      Collection types = pei.getExceptionTypes();
      if (types != null) {
        for (Iterator it2 = types.iterator(); it2.hasNext();) {
          TypeReference type = it2.next();
          if (type != null) {
            InstanceKey ik = getInstanceKeyForPEI(node, peiLoc, type, instanceKeyFactory);
            if (ik == null) {
              continue;
            }
            assert ik instanceof ConcreteTypeKey : "uh oh: need to implement getCaughtException constraints for instance " + ik;
            ConcreteTypeKey ck = (ConcreteTypeKey) ik;
            IClass klass = ck.getType();
            if (PropagationCallGraphBuilder.catches(catchClasses, klass, cha)) {
              system.newConstraint(exceptionVar, getInstanceKeyForPEI(node, peiLoc, type, instanceKeyFactory));
            }
          }
        }
      }
    }
  }

  /**
   * @return true iff there's a unique catch block which catches all exceptions thrown by a certain call site.
   */
  protected static boolean hasUniqueCatchBlock(SSAAbstractInvokeInstruction call, IR ir) {
    ISSABasicBlock[] bb = ir.getBasicBlocksForCall(call.getCallSite());
    if (bb.length == 1) {
      Iterator it = ir.getControlFlowGraph().getExceptionalSuccessors(bb[0]).iterator();
      // check that there's exactly one element in the iterator
      if (it.hasNext()) {
        it.next();
        return (!it.hasNext());
      }
    }
    return false;
  }

  /**
   * precondition: hasUniqueCatchBlock(call,node,cg)
   * 
   * @return the unique pointer key which catches the exceptions thrown by a call
   * @throws IllegalArgumentException if ir == null
   * @throws IllegalArgumentException if call == null
   */
  public PointerKey getUniqueCatchKey(SSAAbstractInvokeInstruction call, IR ir, CGNode node) throws IllegalArgumentException,
      IllegalArgumentException {
    if (call == null) {
      throw new IllegalArgumentException("call == null");
    }
    if (ir == null) {
      throw new IllegalArgumentException("ir == null");
    }
    ISSABasicBlock[] bb = ir.getBasicBlocksForCall(call.getCallSite());
    assert bb.length == 1;
    SSACFG.BasicBlock cb = (BasicBlock) ir.getControlFlowGraph().getExceptionalSuccessors(bb[0]).iterator().next();
    if (cb.isExitBlock()) {
      return getPointerKeyForExceptionalReturnValue(node);
    } else {
      SSACFG.ExceptionHandlerBasicBlock ehbb = (ExceptionHandlerBasicBlock) cb;
      SSAGetCaughtExceptionInstruction ci = ehbb.getCatchInstruction();
      return getPointerKeyForLocal(node, ci.getDef());
    }
  }

  /**
   * @return a List of Instructions that may transfer control to bb via an exceptional edge
   * @throws IllegalArgumentException if ir is null
   */
  public static List getIncomingPEIs(IR ir, ISSABasicBlock bb) {
    if (ir == null) {
    @Override
      throw new IllegalArgumentException("ir is null");
    }
    if (DEBUG) {
      System.err.println("getIncomingPEIs " + bb);
    }
    ControlFlowGraph g = ir.getControlFlowGraph();
    List result = new ArrayList(g.getPredNodeCount(bb));
    for (Iterator it = g.getPredNodes(bb); it.hasNext();) {
      BasicBlock pred = (BasicBlock) it.next();
      if (DEBUG) {
        System.err.println("pred: " + pred);
      }
      if (pred.isEntryBlock())
        continue;
      int index = pred.getLastInstructionIndex();
      SSAInstruction pei = ir.getInstructions()[index];
      // Note: pei might be null if pred is unreachable.
      // TODO: consider pruning CFG for unreachable blocks.
      if (pei != null && pei.isPEI()) {
        if (DEBUG) {
          System.err.println("PEI: " + pei + " index " + index + " PC " + g.getProgramCounter(index));
        }
        result.add(new ProgramCounter(g.getProgramCounter(index)));
      }
    }
    return result;
  }

  /**
   * A visitor that generates constraints based on statements in SSA form.
   */
  protected static class ConstraintVisitor extends SSAInstruction.Visitor {

    /**
     * The governing call graph builder. This field is used instead of an inner class in order to allow more flexible reuse of this
     * visitor in subclasses
     */
    protected final SSAPropagationCallGraphBuilder builder;

    /**
     * The node whose statements we are currently traversing
     */
    protected final CGNode node;

    /**
     * The governing call graph.
     */
    private final ExplicitCallGraph callGraph;

    /**
     * The governing IR
     */
    protected final IR ir;

    /**
     * The governing propagation system, into which constraints are added
     */

     * The basic block currently being processed
     */
    protected ISSABasicBlock basicBlock;

    /**
     * Governing symbol table
     */
    protected final SymbolTable symbolTable;

    /**
     * Def-use information
     */
    protected final DefUse du;

    public ConstraintVisitor(SSAPropagationCallGraphBuilder builder, CGNode node) {
      this.builder = builder;
      this.node = node;

      this.callGraph = builder.getCallGraph();

      this.system = builder.getPropagationSystem();

      this.ir = builder.getCFAContextInterpreter().getIR(node);
      this.symbolTable = this.ir.getSymbolTable();

      this.du = builder.getCFAContextInterpreter().getDU(node);

      assert symbolTable != null;
    }

    protected SSAPropagationCallGraphBuilder getBuilder() {
      return builder;
    }

    protected AnalysisOptions getOptions() {
      return builder.options;
    }

    protected AnalysisCache getAnalysisCache() {
      return builder.getAnalysisCache();
    }

    public PointerKey getPointerKeyForLocal(int valueNumber) {
      return getBuilder().getPointerKeyForLocal(node, valueNumber);
    }

        system.recordImplicitPointsToSet(result);
    public FilteredPointerKey getFilteredPointerKeyForLocal(int valueNumber, FilteredPointerKey.TypeFilter filter) {
      return getBuilder().getFilteredPointerKeyForLocal(node, valueNumber, filter);
    }

    public PointerKey getPointerKeyForReturnValue() {
      return getBuilder().getPointerKeyForReturnValue(node);
    }

    public PointerKey getPointerKeyForExceptionalReturnValue() {
      return getBuilder().getPointerKeyForExceptionalReturnValue(node);
    }

    public PointerKey getPointerKeyForStaticField(IField f) {
      return getBuilder().getPointerKeyForStaticField(f);
    }

    public PointerKey getPointerKeyForInstanceField(InstanceKey I, IField f) {
      return getBuilder().getPointerKeyForInstanceField(I, f);
    }

    public PointerKey getPointerKeyForArrayContents(InstanceKey I) {
      return getBuilder().getPointerKeyForArrayContents(I);
    }

    public InstanceKey getInstanceKeyForAllocation(NewSiteReference allocation) {
      return getBuilder().getInstanceKeyForAllocation(node, allocation);
    }

    public InstanceKey getInstanceKeyForMultiNewArray(NewSiteReference allocation, int dim) {
      return getBuilder().getInstanceKeyForMultiNewArray(node, allocation, dim);
    }

    public  InstanceKey getInstanceKeyForConstant(T S) {
      TypeReference type = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getConstantType(S);
      return getBuilder().getInstanceKeyForConstant(type, S);
    }

    public InstanceKey getInstanceKeyForPEI(ProgramCounter instr, TypeReference type) {
      return getBuilder().getInstanceKeyForPEI(node, instr, type);
    }

    public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
      return getBuilder().getInstanceKeyForClassObject(type);
    }

    public CGNode getTargetForCall(CGNode caller, CallSiteReference site, IClass recv, InstanceKey iKey[]) {
      return getBuilder().getTargetForCall(caller, site, recv, iKey);
    }

    protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumber) {
      return getBuilder().contentsAreInvariant(symbolTable, du, valueNumber);
    }

    protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumber[]) {
      return getBuilder().contentsAreInvariant(symbolTable, du, valueNumber);
    }

    protected InstanceKey[] getInvariantContents(int valueNumber) {
      return getInvariantContents(ir.getSymbolTable(), du, node, valueNumber);
    }

    protected InstanceKey[] getInvariantContents(SymbolTable symbolTable, DefUse du, CGNode node, int valueNumber) {
      return getBuilder().getInvariantContents(symbolTable, du, node, valueNumber, getBuilder());
    }

    protected IClassHierarchy getClassHierarchy() {
      return getBuilder().getClassHierarchy();
    }

    protected boolean hasNoInterestingUses(int vn) {
      return getBuilder().hasNoInterestingUses(node, vn, du);
    }

    protected boolean isRootType(IClass klass) {
      return getBuilder().isRootType(klass);
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitArrayLoad(com.ibm.wala.ssa.SSAArrayLoadInstruction)
     */
    @Override
    public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
      // skip arrays of primitive type
      if (instruction.typeIsPrimitive()) {
        return;
      }
      doVisitArrayLoad(instruction.getDef(), instruction.getArrayRef());
    }

    protected void doVisitArrayLoad(int def, int arrayRef) {
      PointerKey result = getPointerKeyForLocal(def);
      PointerKey arrayRefPtrKey = getPointerKeyForLocal(arrayRef);
      if (hasNoInterestingUses(def)) {
      } else {
        if (contentsAreInvariant(symbolTable, du, arrayRef)) {
          system.recordImplicitPointsToSet(arrayRefPtrKey);
          InstanceKey[] ik = getInvariantContents(arrayRef);
          for (int i = 0; i < ik.length; i++) {
            if (!representsNullType(ik[i])) {
              system.findOrCreateIndexForInstanceKey(ik[i]);
              PointerKey p = getPointerKeyForArrayContents(ik[i]);
              if (p == null) {
              } else {
                system.newConstraint(result, assignOperator, p);
              }
            }
          }
        } else {
          assert !system.isUnified(result);
          assert !system.isUnified(arrayRefPtrKey);
          system.newSideEffect(getBuilder().new ArrayLoadOperator(system.findOrCreatePointsToSet(result)), arrayRefPtrKey);
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitArrayStore(com.ibm.wala.ssa.SSAArrayStoreInstruction)
     */
    public void doVisitArrayStore(int arrayRef, int value) {
      // (requires the creation of assign constraints as
      // the set points-to(a[]) grows.)
      PointerKey valuePtrKey = getPointerKeyForLocal(value);
      PointerKey arrayRefPtrKey = getPointerKeyForLocal(arrayRef);
      // if (!supportFullPointerFlowGraph &&
      // contentsAreInvariant(instruction.getArrayRef())) {
      if (contentsAreInvariant(symbolTable, du, arrayRef)) {
        system.recordImplicitPointsToSet(arrayRefPtrKey);
        InstanceKey[] ik = getInvariantContents(arrayRef);

        for (int i = 0; i < ik.length; i++) {
          if (!representsNullType(ik[i]) && !(ik[i] instanceof ZeroLengthArrayInNode)) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            PointerKey p = getPointerKeyForArrayContents(ik[i]);
            IClass contents = ((ArrayClass) ik[i].getConcreteType()).getElementClass();
            if (p == null) {
            } else {
              if (contentsAreInvariant(symbolTable, du, value)) {
                system.recordImplicitPointsToSet(valuePtrKey);
                InstanceKey[] vk = getInvariantContents(value);
                for (int j = 0; j < vk.length; j++) {
                  system.findOrCreateIndexForInstanceKey(vk[j]);
                  if (vk[j].getConcreteType() != null) {
      }
    }
                    if (getClassHierarchy().isAssignableFrom(contents, vk[j].getConcreteType())) {
                      system.newConstraint(p, vk[j]);
                    }
                  }
                }
              } else {
                if (isRootType(contents)) {
                  system.newConstraint(p, assignOperator, valuePtrKey);
                } else {
                  system.newConstraint(p, getBuilder().filterOperator, valuePtrKey);
                }
              }
            }
          }
        }
      } else {
        if (contentsAreInvariant(symbolTable, du, value)) {
          system.recordImplicitPointsToSet(valuePtrKey);
          InstanceKey[] ik = getInvariantContents(value);
          for (int i = 0; i < ik.length; i++) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            assert !system.isUnified(arrayRefPtrKey);
            system.newSideEffect(getBuilder().new InstanceArrayStoreOperator(ik[i]), arrayRefPtrKey);
          }
        } else {
          system.newSideEffect(getBuilder().new ArrayStoreOperator(system.findOrCreatePointsToSet(valuePtrKey)), arrayRefPtrKey);
        }
      }
    }

    @Override
    public void visitArrayStore(SSAArrayStoreInstruction instruction) {
      // skip arrays of primitive type
      if (instruction.typeIsPrimitive()) {
        return;
      }
      doVisitArrayStore(instruction.getArrayRef(), instruction.getValue());
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitCheckCast(com.ibm.wala.ssa.SSACheckCastInstruction)
     */
    public void visitCheckCast(SSACheckCastInstruction instruction) {
 
      boolean isRoot = false;
    Set types = HashSetFactory.make();
      
      for(TypeReference t : instruction.getDeclaredResultTypes()) {
        IClass cls = getClassHierarchy().lookupClass(t);
        if (cls == null) {
          Warnings.add(CheckcastFailure.create(t));
         return;
        } else {
          if (isRootType(cls)) {
            isRoot = true;
          }
          types.add(cls);
        }
      }
      
      PointerKey result = getFilteredPointerKeyForLocal(instruction.getResult(), new FilteredPointerKey.MultipleClassesFilter(types.toArray(new IClass[ types.size() ])));
      PointerKey value = getPointerKeyForLocal(instruction.getVal());

      if (hasNoInterestingUses(instruction.getDef())) {
        system.recordImplicitPointsToSet(result);
      } else {
        if (contentsAreInvariant(symbolTable, du, instruction.getVal())) {
          system.recordImplicitPointsToSet(value);
          InstanceKey[] ik = getInvariantContents(instruction.getVal());
          for(TypeReference t : instruction.getDeclaredResultTypes()) {
            IClass cls = getClassHierarchy().lookupClass(t);

            if (cls.isInterface()) {
              for (int i = 0; i < ik.length; i++) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                if (getClassHierarchy().implementsInterface(ik[i].getConcreteType(), cls)) {
                  system.newConstraint(result, ik[i]);
                }
              }
            } else {
              for (int i = 0; i < ik.length; i++) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                if (getClassHierarchy().isSubclassOf(ik[i].getConcreteType(), cls)) {
                  system.newConstraint(result, ik[i]);
                }
              }
            }
          }
        } else {
          if (isRoot) {
            system.newConstraint(result, assignOperator, value);
          } else {
            system.newConstraint(result, getBuilder().filterOperator, value);
          }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitReturn(com.ibm.wala.ssa.SSAReturnInstruction)
     */
    @Override
    public void visitReturn(SSAReturnInstruction instruction) {

      // skip returns of primitive type
      if (instruction.returnsPrimitiveType() || instruction.returnsVoid()) {
        return;
      }
      if (DEBUG) {
        System.err.println("visitReturn: " + instruction);
      }

      PointerKey returnValue = getPointerKeyForReturnValue();
      PointerKey result = getPointerKeyForLocal(instruction.getResult());
      if (contentsAreInvariant(symbolTable, du, instruction.getResult())) {
        system.recordImplicitPointsToSet(result);
        InstanceKey[] ik = getInvariantContents(instruction.getResult());
        for (int i = 0; i < ik.length; i++) {
          if (DEBUG) {
            System.err.println("invariant contents: " + returnValue + " " + ik[i]);
          }
          system.newConstraint(returnValue, ik[i]);
        }
      } else {
        system.newConstraint(returnValue, assignOperator, result);
      }
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitGet(com.ibm.wala.ssa.SSAGetInstruction)
     */
    @Override
    public void visitGet(SSAGetInstruction instruction) {
      visitGetInternal(instruction.getDef(), instruction.getRef(), instruction.isStatic(), instruction.getDeclaredField());
    }

    protected void visitGetInternal(int lval, int ref, boolean isStatic, FieldReference field) {
      if (DEBUG) {
        System.err.println("visitGet " + field);
      }

      // skip getfields of primitive type (optimisation)
      if (field.getFieldType().isPrimitiveType()) {
        return;
      }
      PointerKey def = getPointerKeyForLocal(lval);
      assert def != null;

      IField f = getClassHierarchy().resolveField(field);
      if (f == null && callGraph.getFakeRootNode().getMethod().getDeclaringClass().getReference().equals(field.getDeclaringClass())) {
        f = callGraph.getFakeRootNode().getMethod().getDeclaringClass().getField(field.getName());
      }

      if (f == null) {
        return;
      }

      if (hasNoInterestingUses(lval)) {
        system.recordImplicitPointsToSet(def);
      } else {
        if (isStatic) {
          PointerKey fKey = getPointerKeyForStaticField(f);
          system.newConstraint(def, assignOperator, fKey);
          IClass klass = getClassHierarchy().lookupClass(field.getDeclaringClass());
          if (klass == null) {
          } else {
            // side effect of getstatic: may call class initializer
            if (DEBUG) {
              System.err.println("getstatic call class init " + klass);
            }
            processClassInitializer(klass);
          }
        } else {
          PointerKey refKey = getPointerKeyForLocal(ref);
          // if (!supportFullPointerFlowGraph &&
          // contentsAreInvariant(ref)) {
          if (contentsAreInvariant(symbolTable, du, ref)) {
            system.recordImplicitPointsToSet(refKey);
            InstanceKey[] ik = getInvariantContents(ref);
            for (int i = 0; i < ik.length; i++) {
              if (!representsNullType(ik[i])) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                PointerKey p = getPointerKeyForInstanceField(ik[i], f);
                system.newConstraint(def, assignOperator, p);
              }
            }
          } else {
            system.newSideEffect(getBuilder().new GetFieldOperator(f, system.findOrCreatePointsToSet(def)), refKey);
          }
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitPut(com.ibm.wala.ssa.PutInstruction)
     */
    @Override
    public void visitPut(SSAPutInstruction instruction) {
      visitPutInternal(instruction.getVal(), instruction.getRef(), instruction.isStatic(), instruction.getDeclaredField());
    }

    public void visitPutInternal(int rval, int ref, boolean isStatic, FieldReference field) {

      if (DEBUG) {
        System.err.println("visitPut " + field);
      }

      // skip putfields of primitive type
      if (field.getFieldType().isPrimitiveType()) {
        return;
      }
      IField f = getClassHierarchy().resolveField(field);
      if (f == null) {
        if (DEBUG) {
          System.err.println("Could not resolve field " + field);
        }
        Warnings.add(FieldResolutionFailure.create(field));
        return;
      }
      assert f.getFieldTypeReference().getName().equals(field.getFieldType().getName()) :
        "name clash of two fields with the same name but different type: " + f.getReference() + " <=> " + field;
      assert isStatic || !symbolTable.isStringConstant(ref) : "put to string constant shouldn't be allowed?";
      if (isStatic) {
        processPutStatic(rval, field, f);
      } else {
        processPutField(rval, ref, f);
      }
    }

    private void processPutField(int rval, int ref, IField f) {
      assert !f.getFieldTypeReference().isPrimitiveType();
      PointerKey refKey = getPointerKeyForLocal(ref);
        }
      PointerKey rvalKey = getPointerKeyForLocal(rval);
      // if (!supportFullPointerFlowGraph &&
      // contentsAreInvariant(rval)) {
      if (contentsAreInvariant(symbolTable, du, rval)) {
        system.recordImplicitPointsToSet(rvalKey);
        InstanceKey[] ik = getInvariantContents(rval);
        if (contentsAreInvariant(symbolTable, du, ref)) {
          system.recordImplicitPointsToSet(refKey);
          InstanceKey[] refk = getInvariantContents(ref);
          for (int j = 0; j < refk.length; j++) {
            if (!representsNullType(refk[j])) {
              system.findOrCreateIndexForInstanceKey(refk[j]);
              PointerKey p = getPointerKeyForInstanceField(refk[j], f);
              for (int i = 0; i < ik.length; i++) {
                system.newConstraint(p, ik[i]);
              }
            }
          }
        } else {
          for (int i = 0; i < ik.length; i++) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            system.newSideEffect(getBuilder().new InstancePutFieldOperator(f, ik[i]), refKey);
          }
        }
      } else {
        if (contentsAreInvariant(symbolTable, du, ref)) {
          system.recordImplicitPointsToSet(refKey);
          InstanceKey[] refk = getInvariantContents(ref);
          for (int j = 0; j < refk.length; j++) {
            if (!representsNullType(refk[j])) {
              system.findOrCreateIndexForInstanceKey(refk[j]);
              PointerKey p = getPointerKeyForInstanceField(refk[j], f);
              system.newConstraint(p, assignOperator, rvalKey);
            }
          }
        } else {
          if (DEBUG) {
            System.err.println("adding side effect " + f);
          }
          system.newSideEffect(getBuilder().new PutFieldOperator(f, system.findOrCreatePointsToSet(rvalKey)), refKey);
        }
      }
    }

    private void processPutStatic(int rval, FieldReference field, IField f) {
      PointerKey fKey = getPointerKeyForStaticField(f);
      PointerKey rvalKey = getPointerKeyForLocal(rval);

      // if (!supportFullPointerFlowGraph &&
      // contentsAreInvariant(rval)) {
      if (contentsAreInvariant(symbolTable, du, rval)) {
        system.recordImplicitPointsToSet(rvalKey);

        InstanceKey[] ik = getInvariantContents(rval);
        for (int i = 0; i < ik.length; i++) {
          system.newConstraint(fKey, ik[i]);
        }
      } else {
        system.newConstraint(fKey, assignOperator, rvalKey);
      }
      if (DEBUG) {
        System.err.println("visitPut class init " + field.getDeclaringClass() + " " + field);
      }
      // side effect of putstatic: may call class initializer
      IClass klass = getClassHierarchy().lookupClass(field.getDeclaringClass());
      if (klass == null) {
        Warnings.add(FieldResolutionFailure.create(field));
      } else {
        processClassInitializer(klass);
      }
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitInvoke(com.ibm.wala.ssa.InvokeInstruction)
     */
    @Override
    public void visitInvoke(SSAInvokeInstruction instruction) {
      visitInvokeInternal(instruction, new DefaultInvariantComputer());
    }

    protected void visitInvokeInternal(final SSAAbstractInvokeInstruction instruction, InvariantComputer invs) {
      if (DEBUG) {
        System.err.println("visitInvoke: " + instruction);
      }

      PointerKey uniqueCatch = null;
      if (hasUniqueCatchBlock(instruction, ir)) {
        uniqueCatch = getBuilder().getUniqueCatchKey(instruction, ir, node);
      }

      InstanceKey[][] invariantParameters = invs.computeInvariantParameters(instruction);
      if (instruction.getCallSite().isStatic()) {
        for (CGNode n : getBuilder().getTargetsForCall(node, instruction, invariantParameters)) {
          getBuilder().processResolvedCall(node, instruction, n, invariantParameters, uniqueCatch);
          if (DEBUG) {
            System.err.println("visitInvoke class init " + n);
          }

          // side effect of invoke: may call class initializer
          processClassInitializer(n.getMethod().getDeclaringClass());
        }
      } else {
        // Add a side effect that will fire when we determine a value
        // for a dispatch parameter. This side effect will create a new node
        // and new constraints based on the new callee context.
        IntSet params = getBuilder().getContextSelector().getRelevantParameters(node, instruction.getCallSite());
        if (! params.contains(0)) {
          params = IntSetUtil.makeMutableCopy(params);
          ((MutableIntSet)params).add(0);
        }
        final int vns[] = new int[ params.size() ];
        params.foreach(new IntSetAction() {
          private int i = 0;
          public void act(int x) {
            vns[i++] = instruction.getUse(x);
          }
        });
        
        if (contentsAreInvariant(symbolTable, du, vns)) {
          for(CGNode n : getBuilder().getTargetsForCall(node, instruction, invariantParameters)) {
              getBuilder().processResolvedCall(node, instruction, n, invariantParameters, uniqueCatch);
              // side effect of invoke: may call class initializer
              processClassInitializer(n.getMethod().getDeclaringClass());
          }
        } else {
          if (DEBUG) {
            System.err.println("Add side effect, dispatch to " + instruction + " for " + params);
          }
 
          final List pks = new ArrayList(params.size());
          params.foreach(new IntSetAction() {
            public void act(int x) {
              if (!contentsAreInvariant(symbolTable, du, instruction.getUse(x))) {
                pks.add(getBuilder().getPointerKeyForLocal(node, instruction.getUse(x)));
              }
            }
          });
   
          DispatchOperator dispatchOperator = getBuilder().new DispatchOperator(instruction, node,
              invariantParameters, uniqueCatch, params);
          system.newSideEffect(dispatchOperator, pks.toArray(new PointerKey[pks.size()]));
        }
      }
    }
    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitNew(com.ibm.wala.ssa.NewInstruction)
     */
    @Override
    public void visitNew(SSANewInstruction instruction) {
      InstanceKey iKey = getInstanceKeyForAllocation(instruction.getNewSite());

      if (iKey == null) {
        // something went wrong. I hope someone raised a warning.
        return;
      }
      PointerKey def = getPointerKeyForLocal(instruction.getDef());
      IClass klass = iKey.getConcreteType();

      if (DEBUG) {
        System.err.println("visitNew: " + instruction + " " + iKey + " " + system.findOrCreateIndexForInstanceKey(iKey));
      }

      if (klass == null) {
        if (DEBUG) {
          System.err.println("Resolution failure: " + instruction);
        }
        return;
      }

      if (!contentsAreInvariant(symbolTable, du, instruction.getDef())) {
        system.newConstraint(def, iKey);
      } else {
        system.findOrCreateIndexForInstanceKey(iKey);
        system.recordImplicitPointsToSet(def);
      }

      // side effect of new: may call class initializer
      if (DEBUG) {
        System.err.println("visitNew call clinit: " + klass);
      }
      processClassInitializer(klass);

      // add instance keys and pointer keys for array contents
      int dim = 0;
      InstanceKey lastInstance = iKey;
      while (klass != null && klass.isArrayClass()) {
        klass = ((ArrayClass) klass).getElementClass();
        // klass == null means it's a primitive
        if (klass != null && klass.isArrayClass()) {
          if (instruction.getNumberOfUses() <= (dim + 1)) {
            break;
          }
          int sv = instruction.getUse(dim + 1);
          if (ir.getSymbolTable().isIntegerConstant(sv)) {
            Integer c = (Integer) ir.getSymbolTable().getConstantValue(sv);
            if (c.intValue() == 0) {
              break;
            }
          }
          InstanceKey ik = getInstanceKeyForMultiNewArray(instruction.getNewSite(), dim);
          PointerKey pk = getPointerKeyForArrayContents(lastInstance);
          if (DEBUG_MULTINEWARRAY) {
            System.err.println("multinewarray constraint: ");
            System.err.println("   pk: " + pk);
            System.err.println("   ik: " + system.findOrCreateIndexForInstanceKey(ik) + " concrete type " + ik.getConcreteType()
                + " is " + ik);
            System.err.println("   klass:" + klass);
          }
          system.newConstraint(pk, ik);
          lastInstance = ik;
          dim++;
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitThrow(com.ibm.wala.ssa.ThrowInstruction)
     */
    @Override
    public void visitThrow(SSAThrowInstruction instruction) {
      // don't do anything: we handle exceptional edges
      // in a separate pass
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitGetCaughtException(com.ibm.wala.ssa.GetCaughtExceptionInstruction)
     */
    @Override
    public void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction) {
      List peis = getIncomingPEIs(ir, getBasicBlock());
      PointerKey def = getPointerKeyForLocal(instruction.getDef());
      // SJF: we don't optimize based on dead catch blocks yet ... it's a little
      // tricky due interaction with the SINGLE_USE optimization which directly
      // shoves exceptional return values from calls into exception vars.
      // it may not be worth doing this.
      // if (hasNoInterestingUses(instruction.getDef(), du)) {
      // solver.recordImplicitPointsToSet(def);
      // } else {
      Set types = getCaughtExceptionTypes(instruction, ir);
      getBuilder().addExceptionDefConstraints(ir, du, node, peis, def, types);
      // }
    }

    /**
     * TODO: What is this doing? Document me!
     */
    private int booleanConstantTest(SSAConditionalBranchInstruction c, int v) {
      int result = 0;

      // right for OPR_eq
      if ((symbolTable.isZeroOrFalse(c.getUse(0)) && c.getUse(1) == v)
          || (symbolTable.isZeroOrFalse(c.getUse(1)) && c.getUse(0) == v)) {
        result = -1;
      } else if ((symbolTable.isOneOrTrue(c.getUse(0)) && c.getUse(1) == v)
          || (symbolTable.isOneOrTrue(c.getUse(1)) && c.getUse(0) == v)) {
        result = 1;
      }

      if (c.getOperator() == ConditionalBranchInstruction.Operator.NE) {
        result = -result;
      }

      return result;
    }

    private int nullConstantTest(SSAConditionalBranchInstruction c, int v) {
      if ((symbolTable.isNullConstant(c.getUse(0)) && c.getUse(1) == v)
          || (symbolTable.isNullConstant(c.getUse(1)) && c.getUse(0) == v)) {
        if (c.getOperator() == ConditionalBranchInstruction.Operator.EQ) {
          return 1;
        } else {
          return -1;
        }
      } else {
        return 0;
      }
    }

    @Override
    public void visitPhi(SSAPhiInstruction instruction) {
      if (ir.getMethod() instanceof AbstractRootMethod) {
      } else {
        PointerKey dst = getPointerKeyForLocal(instruction.getDef());
        if (hasNoInterestingUses(instruction.getDef())) {
          system.recordImplicitPointsToSet(dst);
        } else {
          for (int i = 0; i < instruction.getNumberOfUses(); i++) {
            PointerKey use = getPointerKeyForLocal(instruction.getUse(i));
            if (contentsAreInvariant(symbolTable, du, instruction.getUse(i))) {
              system.recordImplicitPointsToSet(use);
              InstanceKey[] ik = getInvariantContents(instruction.getUse(i));
              for (int j = 0; j < ik.length; j++) {
                system.newConstraint(dst, ik[j]);
              }
            } else {
              system.newConstraint(dst, assignOperator, use);
            }
          }
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitPi(com.ibm.wala.ssa.SSAPiInstruction)
     */
    @Override
    public void visitPi(SSAPiInstruction instruction) {
      int dir;

      if (hasNoInterestingUses(instruction.getDef())) {
        PointerKey dst = getPointerKeyForLocal(instruction.getDef());
        system.recordImplicitPointsToSet(dst);
      } else {
        ControlFlowGraph cfg = ir.getControlFlowGraph();
        if (com.ibm.wala.cfg.Util.endsWithConditionalBranch(cfg, getBasicBlock()) && cfg.getSuccNodeCount(getBasicBlock()) == 2) {
          SSAConditionalBranchInstruction cond = (SSAConditionalBranchInstruction) com.ibm.wala.cfg.Util.getLastInstruction(cfg,
              getBasicBlock());
          SSAInstruction cause = instruction.getCause();
          BasicBlock target = (BasicBlock) cfg.getNode(instruction.getSuccessor());

          if ((cause instanceof SSAInstanceofInstruction)) {
            int direction = booleanConstantTest(cond, cause.getDef());
            if (direction != 0) {
              TypeReference type = ((SSAInstanceofInstruction) cause).getCheckedType();
              IClass cls = getClassHierarchy().lookupClass(type);
              if (cls == null) {
                PointerKey dst = getPointerKeyForLocal(instruction.getDef());
                addPiAssignment(dst, instruction.getVal());
              } else {
                PointerKey dst = getFilteredPointerKeyForLocal(instruction.getDef(), new FilteredPointerKey.SingleClassFilter(cls));
                PointerKey src = getPointerKeyForLocal(instruction.getVal());
                if ((target == com.ibm.wala.cfg.Util.getTakenSuccessor(cfg, getBasicBlock()) && direction == 1)
                    || (target == com.ibm.wala.cfg.Util.getNotTakenSuccessor(cfg, getBasicBlock()) && direction == -1)) {
                  system.newConstraint(dst, getBuilder().filterOperator, src);
                } else {
                  system.newConstraint(dst, getBuilder().inverseFilterOperator, src);
                }
              }
            }
          } else if ((dir = nullConstantTest(cond, instruction.getVal())) != 0) {
            if ((target == com.ibm.wala.cfg.Util.getTakenSuccessor(cfg, getBasicBlock()) && dir == -1)
                || (target == com.ibm.wala.cfg.Util.getNotTakenSuccessor(cfg, getBasicBlock()) && dir == 1)) {
              PointerKey dst = getPointerKeyForLocal(instruction.getDef());
              addPiAssignment(dst, instruction.getVal());
            }
          } else {
            PointerKey dst = getPointerKeyForLocal(instruction.getDef());
            addPiAssignment(dst, instruction.getVal());
          }
        } else {
          PointerKey dst = getPointerKeyForLocal(instruction.getDef());
          addPiAssignment(dst, instruction.getVal());
        }
      }
    }

    /**
     * Add a constraint to the system indicating that the contents of local src flows to dst, with no special type filter.
     */
    private void addPiAssignment(PointerKey dst, int src) {
      PointerKey srcKey = getPointerKeyForLocal(src);
      if (contentsAreInvariant(symbolTable, du, src)) {
        system.recordImplicitPointsToSet(srcKey);
        InstanceKey[] ik = getInvariantContents(src);
        for (int j = 0; j < ik.length; j++) {
          system.newConstraint(dst, ik[j]);
        system.newConstraint(dst, assignOperator, srcKey);
      }

    }

    public ISSABasicBlock getBasicBlock() {
      return basicBlock;
    }

    /**
     * The calling loop must call this in each iteration!
     */
    public void setBasicBlock(ISSABasicBlock block) {
      basicBlock = block;
    }

    protected interface InvariantComputer {
 
      InstanceKey[][] computeInvariantParameters(SSAAbstractInvokeInstruction call);

    }

    public class DefaultInvariantComputer implements InvariantComputer {
    /**
     * Side effect: records invariant parameters as implicit points-to-sets.
     * 
     * @return if non-null, then result[i] holds the set of instance keys which may be passed as the ith parameter. (which must be
     *         invariant)
     */
    public InstanceKey[][] computeInvariantParameters(SSAAbstractInvokeInstruction call) {
      InstanceKey[][] constParams = null;
      for (int i = 0; i < call.getNumberOfUses(); i++) {
        // not sure how getUse(i) <= 0 .. dead code?
        // TODO: investigate
        if (call.getUse(i) > 0) {
          if (contentsAreInvariant(symbolTable, du, call.getUse(i))) {
            system.recordImplicitPointsToSet(getPointerKeyForLocal(call.getUse(i)));
            if (constParams == null) {
              constParams = new InstanceKey[call.getNumberOfUses()][];
            }
            constParams[i] = getInvariantContents(call.getUse(i));
            for (int j = 0; j < constParams[i].length; j++) {
              system.findOrCreateIndexForInstanceKey(constParams[i][j]);
            }
          }
        }
      }
      return constParams;
    }
    }
    
    @Override
    public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
      PointerKey def = getPointerKeyForLocal(instruction.getDef());
      assert instruction.getType() == TypeReference.JavaLangClass;
      InstanceKey iKey = getInstanceKeyForClassObject((TypeReference) instruction.getToken());
    private final SSAAbstractInvokeInstruction call;
      IClass klass = getClassHierarchy().lookupClass((TypeReference) instruction.getToken());
      if (klass != null) {
        processClassInitializer(klass);
      }

      if (!contentsAreInvariant(symbolTable, du, instruction.getDef())) {
        system.newConstraint(def, iKey);
      } else {
        system.findOrCreateIndexForInstanceKey(iKey);
        system.recordImplicitPointsToSet(def);
      }
    }

    /**
     * TODO: lift most of this logic to PropagationCallGraphBuilder
     * 
     * Add a call to the class initializer from the root method.
     */
    private void processClassInitializer(IClass klass) {

      assert klass != null;

      if (!getBuilder().getOptions().getHandleStaticInit()) {
        return;
      }

      if (getBuilder().clinitVisited.contains(klass)) {
        return;
      }
      getBuilder().clinitVisited.add(klass);

      if (klass.getClassInitializer() != null) {
        if (DEBUG) {
          System.err.println("process class initializer for " + klass);
        }

        // add an invocation from the fake root method to the 
        AbstractRootMethod fakeWorldClinitMethod = (AbstractRootMethod) callGraph.getFakeWorldClinitNode().getMethod();
        MethodReference m = klass.getClassInitializer().getReference();
        CallSiteReference site = CallSiteReference.make(1, m, IInvokeInstruction.Dispatch.STATIC);
        IMethod targetMethod = getOptions().getMethodTargetSelector().getCalleeTarget(callGraph.getFakeRootNode(), site, null);

        if (targetMethod != null) {
          CGNode target = getTargetForCall(callGraph.getFakeRootNode(), site, null, null);
          if (target != null && callGraph.getPredNodeCount(target) == 0) {
            SSAAbstractInvokeInstruction s = fakeWorldClinitMethod.addInvocation(new int[0], site);
            PointerKey uniqueCatch = getBuilder().getPointerKeyForExceptionalReturnValue(callGraph.getFakeRootNode());
            getBuilder().processResolvedCall(callGraph.getFakeWorldClinitNode(), s, target, null, uniqueCatch);
          }
        }
      }

      IClass sc = klass.getSuperclass();
      if (sc != null) {
        processClassInitializer(sc);
      }
    }
  }

  /**
   * Add constraints for a call site after we have computed a reachable target for the dispatch
   * 
   * Side effect: add edge to the call graph.
   * 
   * @param instruction
   * @param constParams if non-null, then constParams[i] holds the set of instance keys that are passed as param i, or null if param
   *          i is not invariant
   * @param uniqueCatchKey if non-null, then this is the unique PointerKey that catches all exceptions from this call site.
   */
  @SuppressWarnings("deprecation")
  private void processResolvedCall(CGNode caller, SSAAbstractInvokeInstruction instruction, CGNode target,
      InstanceKey[][] constParams, PointerKey uniqueCatchKey) {

    if (DEBUG) {
      System.err.println("processResolvedCall: " + caller + " ," + instruction + " , " + target);
    }

    if (DEBUG) {
      System.err.println("addTarget: " + caller + " ," + instruction + " , " + target);
    }
    caller.addTarget(instruction.getCallSite(), target);

    if (FakeRootMethod.isFakeRootMethod(caller.getMethod().getReference())) {
      if (entrypointCallSites.contains(instruction.getCallSite())) {
        callGraph.registerEntrypoint(target);
      }
    }

    if (!haveAlreadyVisited(target)) {
      markDiscovered(target);
    }
    
    processCallingConstraints(caller, instruction, target, constParams, uniqueCatchKey);
  }
  

  protected void processCallingConstraints(CGNode caller, SSAAbstractInvokeInstruction instruction, CGNode target,
      InstanceKey[][] constParams, PointerKey uniqueCatchKey) {
    // TODO: i'd like to enable this optimization, but it's a little tricky
    // to recover the implicit points-to sets with recursion. TODO: don't
    // be lazy and code the recursive logic to enable this.
    // if (hasNoInstructions(target)) {
    // // record points-to sets for formals implicitly .. computed on
    // // demand.
    // // TODO: generalize this by using hasNoInterestingUses on parameters.
    // // however .. have to be careful to cache results in that case ... don't
    // // want
    // // to recompute du each time we process a call to Object. !
    // for (int i = 0; i < instruction.getNumberOfUses(); i++) {
    // // we rely on the invariant that the value number for the ith parameter
    // // is i+1
    // final int vn = i + 1;
    // PointerKey formal = getPointerKeyForLocal(target, vn);
    // if (target.getMethod().getParameterType(i).isReferenceType()) {
    // system.recordImplicitPointsToSet(formal);
    // }
    // }
    // } else {
    // generate contraints from parameter passing
    int nUses = instruction.getNumberOfParameters();
    int nExpected = target.getMethod().getNumberOfParameters();

    /*
     * int nExpected = target.getMethod().getReference().getNumberOfParameters(); if (!target.getMethod().isStatic() &&
     * !target.getMethod().isClinit()) { nExpected++; }
     */

    if (nUses != nExpected) {
      // some sort of unverifiable code mismatch. give up.
      return;
    }

    // we're a little sloppy for now ... we don't filter calls to
    // java.lang.Object.
    /*
    // TODO: we need much more precise filters than cones in order to handle
    // the various types of dispatch logic. We need a filter that expresses
    // "the set of types s.t. x.foo resolves to y.foo."
    for (int i = 0; i < instruction.getNumberOfParameters(); i++) {
      if (target.getMethod().getParameterType(i).isReferenceType()) {
        PointerKey formal = getTargetPointerKey(target, i);
        if (constParams != null && constParams[i] != null) {
          InstanceKey[] ik = constParams[i];
          for (int j = 0; j < ik.length; j++) {
            system.newConstraint(formal, ik[j]);
          }
        } else {
          if (instruction.getUse(i) < 0) {
            Assertions.UNREACHABLE("unexpected " + instruction + " in " + caller);
          }
          PointerKey actual = getPointerKeyForLocal(caller, instruction.getUse(i));
          if (formal instanceof FilteredPointerKey) {
            system.newConstraint(formal, filterOperator, actual);
          } else {
            system.newConstraint(formal, assignOperator, actual);
          }
        }
      }
    }

    // generate contraints from return value.
    if (instruction.hasDef() && instruction.getDeclaredResultType().isReferenceType()) {
      PointerKey result = getPointerKeyForLocal(caller, instruction.getDef());
      PointerKey ret = getPointerKeyForReturnValue(target);
      system.newConstraint(result, assignOperator, ret);
    }
    // generate constraints from exception return value.
    PointerKey e = getPointerKeyForLocal(caller, instruction.getException());
    PointerKey er = getPointerKeyForExceptionalReturnValue(target);
    if (SHORT_CIRCUIT_SINGLE_USES && uniqueCatchKey != null) {
      // e has exactly one use. so, represent e implicitly
      system.newConstraint(uniqueCatchKey, assignOperator, er);
    } else {
      system.newConstraint(e, assignOperator, er);
    }
    // }
  }

  /**
   * An operator to fire when we discover a potential new callee for a virtual or interface call site.
   * 
   * This operator will create a new callee context and constraints if necessary.
   */
  final class DispatchOperator extends AbstractOperator implements IPointerOperator {
    private final CGNode node;

    private final InstanceKey[][] constParams;

    private final PointerKey uniqueCatch;

    /**
     * relevant parameter indices for the registered {@link ContextSelector}
     * 
     * @see ContextSelector#getRelevantParameters(CGNode, CallSiteReference)
     */
    private final int[] dispatchIndices;
    
    /**
     * The set of instance keys that have already been processed.
     * previousPtrs[i] contains the processed instance keys for parameter
     * position dispatchIndices[i]
     */
    final private MutableIntSet[] previousPtrs;
    
    /**
     * @param call
     * @param node
     * @param constParams if non-null, then constParams[i] holds the String constant that is passed as param i, or null if param i
     *          is not a String constant
     */
    DispatchOperator(SSAAbstractInvokeInstruction call, CGNode node, InstanceKey[][] constParams,
        PointerKey uniqueCatch, IntSet dispatchIndices) {
      this.call = call;
      this.node = node;
      this.constParams = constParams;
      this.uniqueCatch = uniqueCatch;
      this.dispatchIndices = IntSetUtil.toArray(dispatchIndices);
      // we better always be interested in the receiver
      assert this.dispatchIndices[0] == 0;
      previousPtrs = new MutableIntSet[dispatchIndices.size()];
      for(int i = 0; i < previousPtrs.length; i++) {
        previousPtrs[i] = IntSetUtil.getDefaultIntSetFactory().make();
      }
    }


     * @see com.ibm.wala.dataflow.fixpoint.UnaryOperator#evaluate(com.ibm.wala.dataflow.fixpoint.IVariable,
     * com.ibm.wala.dataflow.fixpoint.IVariable)
     */
    @Override
    public byte evaluate(PointsToSetVariable lhs, final PointsToSetVariable[] rhs) {
      assert dispatchIndices.length >= rhs.length : "bad operator at " + call;
      final MutableBoolean sideEffect = new MutableBoolean();
      
      final MutableIntSet receiverVals;
      if (constParams != null && constParams[0] != null) {
        receiverVals = IntSetUtil.make();
        for(InstanceKey ik : constParams[0]) {
          receiverVals.add(system.getInstanceIndex(ik));
        }
      } else {
        receiverVals = rhs[0].getValue();
      }
      
      if (receiverVals == null) {
        // this constraint was put on the work list, probably by
        // initialization,
        // even though the right-hand-side is empty.
        // TODO: be more careful about what goes on the worklist to
        // avoid this.
        if (DEBUG) {
          System.err.println("EVAL dispatch with value null");
        }
        return NOT_CHANGED;        
        
      }
      // we handle the parameter positions one by one, rather than enumerating
      // the cartesian product of possibilities. this disallows
      // context-sensitivity policies like true CPA, but is necessary for
      // performance.
      InstanceKey keys[] = new InstanceKey[constParams == null? dispatchIndices[dispatchIndices.length-1]+1: constParams.length];
      // determine whether we're handling a new receiver; used later
      // to check for redundancy
      boolean newReceiver = !receiverVals.isSubset(previousPtrs[0]);
      // keep separate rhsIndex, since it doesn't advance for constant
      // parameters
      int rhsIndex = (constParams != null && constParams[0] != null)? 0: 1;
      // we start at index 1 since we need to handle the receiver specially; see
      // below
      for (int index = 1; index < dispatchIndices.length; index++) {
        try {
          MonitorUtil.throwExceptionIfCanceled(monitor);
        } catch (CancelException e) {
          throw new CancelRuntimeException(e);
        }
        int paramIndex = dispatchIndices[index];
        assert keys[paramIndex] == null;
        final MutableIntSet prevAtIndex = previousPtrs[index];
        if (constParams != null && constParams[paramIndex] != null) {
          // we have a constant parameter.  only need to propagate again if we've never done it before or if we have a new receiver
          if (newReceiver || prevAtIndex.isEmpty()) {
            for(int i = 0; i < constParams[paramIndex].length; i++) {
              keys[paramIndex] = constParams[paramIndex][i];
              handleAllReceivers(receiverVals,keys, sideEffect);
              int ii = system.instanceKeys.getMappedIndex(constParams[paramIndex][i]);
              prevAtIndex.add(ii);
            }            
          }
        } else { // non-constant parameter
          PointsToSetVariable v = rhs[rhsIndex];
          if (v.getValue() != null) {
            IntIterator ptrs = v.getValue().intIterator();
            while (ptrs.hasNext()) {
              int ptr = ptrs.next();
              if (newReceiver || !prevAtIndex.contains(ptr)) {
                keys[paramIndex] = system.getInstanceKey(ptr);
                handleAllReceivers(receiverVals,keys, sideEffect);
                prevAtIndex.add(ptr);
              }
            }
          } 
          rhsIndex++;
        }
        keys[paramIndex] = null;
      }
      if (newReceiver && !sideEffect.b) {
        // we have a new receiver value, and it wasn't propagated at all,
        // so propagate it now
        handleAllReceivers(receiverVals, keys, sideEffect);
        previousPtrs[0].addAll(receiverVals);
      }

      byte sideEffectMask = sideEffect.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    private void handleAllReceivers(MutableIntSet receiverVals, InstanceKey[] keys, MutableBoolean sideEffect) {
      assert keys[0] == null;
      IntIterator receiverIter = receiverVals.intIterator();
      while (receiverIter.hasNext()) {
        final int rcvr = receiverIter.next();
        keys[0] = system.getInstanceKey(rcvr);
        if (clone2Assign) {
          // for efficiency: assume that only call sites that reference
          // clone() might dispatch to clone methods
          if (call.getCallSite().getDeclaredTarget().getSelector().equals(cloneSelector)) {
            IClass recv = (keys[0] != null) ? keys[0].getConcreteType() : null;
            IMethod targetMethod = getOptions().getMethodTargetSelector().getCalleeTarget(node, call.getCallSite(), recv);
            if (targetMethod != null && targetMethod.getReference().equals(CloneInterpreter.CLONE)) {
              // treat this call to clone as an assignment
              PointerKey result = getPointerKeyForLocal(node, call.getDef());
              PointerKey receiver = getPointerKeyForLocal(node, call.getReceiver());
              system.newConstraint(result, assignOperator, receiver);
              return;
            }
          }
        }
        CGNode target = getTargetForCall(node, call.getCallSite(), keys[0].getConcreteType(), keys);
        if (target == null) {
          // This indicates an error; I sure hope getTargetForCall
          // raised a warning about this!
          if (DEBUG) {
            System.err.println("Warning: null target for call " + call);
          }
        } else {
          IntSet targets = getCallGraph().getPossibleTargetNumbers(node, call.getCallSite());
          if (targets != null && targets.contains(target.getGraphNodeId())) {
            // do nothing; we've previously discovered and handled this
            // receiver for this call site.
          } else {
            // process the newly discovered target for this call
            sideEffect.b = true;
            processResolvedCall(node, call, target, constParams, uniqueCatch);
            if (!haveAlreadyVisited(target)) {
              markDiscovered(target);
            }
          }
        }
      }
      keys[0] = null;
    }



    @Override
    public String toString() {
      return "Dispatch to " + call + " in node " + node;
    }

    @Override
    public int hashCode() {
      return node.hashCode() + 90289 * call.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      // note that these are not necessarily canonical, since
      // with synthetic factories we may regenerate constraints
      // many times. TODO: change processing of synthetic factories
      // so that we guarantee to insert each dispatch equation
      // only once ... if this were true we could optimize this
      // with reference equality

      // instanceof is OK because this class is final
      if (o instanceof DispatchOperator) {
        DispatchOperator other = (DispatchOperator) o;
        return node.equals(other.node) && call.equals(other.call) && Arrays.deepEquals(constParams, other.constParams);
      } else {
        return false;
      }
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  protected void iterateCrossProduct(final CGNode caller, final SSAAbstractInvokeInstruction call, IntSet parameters,
      final InstanceKey[][] invariants, final VoidFunction f) {
    final int params[] = IntSetUtil.toArray(parameters);
    final InstanceKey[] keys = new InstanceKey[call.getNumberOfParameters()];
    final CallSiteReference site = call.getCallSite();
    new Object() {
      private void rec(final int pi) {
        if (pi == params.length) {
          f.apply(keys);
        } else {
          final int p = params[pi];
          int vn = call.getUse(p);
          PointerKey var = getPointerKeyForLocal(caller, vn);
          InstanceKey[] ik = invariants != null ? invariants[p] : null;
          if (ik != null) {
            if (ik.length > 0) {
              for (int i = 0; i < ik.length; i++) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                keys[p] = ik[i];
                rec(pi + 1);
              }
            } else {
              if (!site.isDispatch() || p != 0) {
                keys[p] = null;
                rec(pi + 1);
              }
            }
          } else {
            IntSet s = system.findOrCreatePointsToSet(var).getValue();
            if (s != null && !s.isEmpty()) {
              s.foreach(new IntSetAction() {
                public void act(int x) {
                  keys[p] = system.getInstanceKey(x);
                  rec(pi + 1);
                }
              });
            } else {
              if (!site.isDispatch() || p != 0) {
                keys[p] = null;
                rec(pi + 1);
              }
            }
          }
        }
      }
    }.rec(0);
  }
  
  protected Set getTargetsForCall(final CGNode caller, final SSAAbstractInvokeInstruction instruction, InstanceKey[][] invs) {
    // This method used to take a CallSiteReference as a parameter, rather than
    // an SSAAbstractInvokeInstruction. This was bad, since it's
    // possible for multiple invoke instructions with different actual
    // parameters to be associated with a single CallSiteReference. Changed
    // to take the invoke instruction as a parameter instead, since invs is
    // associated with the instruction
    final CallSiteReference site = instruction.getCallSite();
    IntSet params = contextSelector.getRelevantParameters(caller, site);
    if (!site.isStatic() && !params.contains(0)) {
      params = IntSetUtil.makeMutableCopy(params);
      ((MutableIntSet)params).add(0);
    }
    final Set targets = HashSetFactory.make();
    VoidFunction f = new VoidFunction() {
      public void apply(InstanceKey[] v) {
        IClass recv = null;
        if (site.isDispatch()) {
          recv = v[0].getConcreteType();
        }
        CGNode target = getTargetForCall(caller, site, recv, v);
        if (target != null) {
          targets.add(target);
        }
      }
    };
    iterateCrossProduct(caller, instruction, params, invs, f);
     return targets;
  }

  public boolean hasNoInterestingUses(CGNode node, int vn, DefUse du) {

    if (du == null) {
      throw new IllegalArgumentException("du is null");
    }
    if (vn <= 0) {
      throw new IllegalArgumentException("v is invalid: " + vn);
    }
    // todo: enhance this by solving a dead-code elimination
    // problem.
    InterestingVisitor v = makeInterestingVisitor(node, vn);
    for (Iterator it = du.getUses(v.vn); it.hasNext();) {
      SSAInstruction s = (SSAInstruction) it.next();
      s.visit(v);
      if (v.bingo) {
        return false;
      }
    }
    return true;
  }

  protected InterestingVisitor makeInterestingVisitor(CGNode node, int vn) {
    return new InterestingVisitor(vn);
  }

  /**
   * sets bingo to true when it visits an interesting instruction
   */
  protected static class InterestingVisitor extends SSAInstruction.Visitor {
    protected final int vn;

    protected InterestingVisitor(int vn) {
      this.vn = vn;
    }

    protected boolean bingo = false;

    @Override
    public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
      if (!instruction.typeIsPrimitive() && instruction.getArrayRef() == vn) {
        bingo = true;
      }
    }

    @Override
    public void visitArrayStore(SSAArrayStoreInstruction instruction) {
      if (!instruction.typeIsPrimitive() && (instruction.getArrayRef() == vn || instruction.getValue() == vn)) {
        bingo = true;
      }
    }

    @Override
    public void visitCheckCast(SSACheckCastInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitGet(SSAGetInstruction instruction) {
      FieldReference field = instruction.getDeclaredField();
      if (!field.getFieldType().isPrimitiveType()) {
        bingo = true;
      }
    }

    @Override
    public void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitInvoke(SSAInvokeInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitPhi(SSAPhiInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitPi(SSAPiInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitPut(SSAPutInstruction instruction) {
      FieldReference field = instruction.getDeclaredField();
      if (!field.getFieldType().isPrimitiveType()) {
        bingo = true;
      }
    }

    @Override
    public void visitReturn(SSAReturnInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitThrow(SSAThrowInstruction instruction) {
      bingo = true;
    }
  }

  /**
   * TODO: enhance this logic using type inference
   * 
   * @param instruction
   * @return true if we need to filter the receiver type to account for virtual dispatch
   */
  @SuppressWarnings("unused")
  private boolean needsFilterForReceiver(SSAAbstractInvokeInstruction instruction, CGNode target) {

    FilteredPointerKey.TypeFilter f = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.PARAMETERS[0]);

    if (f != null) {
      // the context selects a particular concrete type for the receiver.
      // we need to filter, unless the declared receiver type implies the
      // concrete type (TODO: need to implement this optimization)
      return true;
    }

    // don't need to filter for invokestatic
    if (instruction.getCallSite().isStatic() || instruction.getCallSite().isSpecial()) {
      return false;
    }

    MethodReference declaredTarget = instruction.getDeclaredTarget();
    IMethod resolvedTarget = getClassHierarchy().resolveMethod(declaredTarget);
    if (resolvedTarget == null) {
      // there's some problem that will be flagged as a warning
      return true;
    }

    return true;
  }

  private boolean isRootType(IClass klass) {
    return klass.getClassHierarchy().isRootClass(klass);
  }

  @SuppressWarnings("unused")
  private boolean isRootType(FilteredPointerKey.TypeFilter filter) {
    if (filter instanceof FilteredPointerKey.SingleClassFilter) {
      return isRootType(((FilteredPointerKey.SingleClassFilter) filter).getConcreteType());
    } else {
      return false;
    }
  }

  /**
   * TODO: enhance this logic using type inference TODO!!!: enhance filtering to consider concrete types, not just cones.
   * precondition: needs Filter
   * 
   * @param target
   * @return an IClass which represents
   */
  protected PointerKey getTargetPointerKey(CGNode target, int index) {
    int vn;
    if (target.getIR() != null) {
      vn = target.getIR().getSymbolTable().getParameter(index);
    } else {
      vn = index+1;
    }

    FilteredPointerKey.TypeFilter filter = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.PARAMETERS[index]);
    if (filter != null && !filter.isRootFilter()) {
        return pointerKeyFactory.getFilteredPointerKeyForLocal(target, vn, filter);
    
    } else if (index == 0 && !target.getMethod().isStatic()) {
      // the context does not select a particular concrete type for the
      // receiver, so use the type of the method
      IClass C = getReceiverClass(target.getMethod());
      if (C.getClassHierarchy().getRootClass().equals(C)) {
        return pointerKeyFactory.getPointerKeyForLocal(target, vn);        
      } else {
        return pointerKeyFactory.getFilteredPointerKeyForLocal(target, vn, new FilteredPointerKey.SingleClassFilter(C));
      }
      
    } else {
      return pointerKeyFactory.getPointerKeyForLocal(target, vn);
    }
  }

  /**
   * @param method
   * @return the receiver class for this method.
   */
  private IClass getReceiverClass(IMethod method) {
    TypeReference formalType = method.getParameterType(0);
    IClass C = getClassHierarchy().lookupClass(formalType);
    if (method.isStatic()) {
      Assertions.UNREACHABLE("asked for receiver of static method " + method);
    }
    if (C == null) {
      Assertions.UNREACHABLE("no class found for " + formalType + " recv of " + method);
    }
    return C;
  }

  /**
   * A value is "invariant" if we can figure out the instances it can ever point to locally, without resorting to propagation.
   * 
   * @param valueNumber
   * @return true iff the contents of the local with this value number can be deduced locally, without propagation
   */
  protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumber) {
    if (isConstantRef(symbolTable, valueNumber)) {
      return true;
    } else if (SHORT_CIRCUIT_INVARIANT_SETS) {
      SSAInstruction def = du.getDef(valueNumber);
      if (def instanceof SSANewInstruction) {
        return true;
      } else {
        return false;
      }
    } else {
      return false;
    }
  }

  protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumbers[]) {
    for(int i = 0; i < valueNumbers.length; i++) {
      if (! contentsAreInvariant(symbolTable, du, valueNumbers[i])) {
        return false;
      }
    }
    return true;
  }
  
  /**
   * precondition:contentsAreInvariant(valueNumber)
   * 
   * @param valueNumber
   * @return the complete set of instances that the local with vn=valueNumber may point to.
   */
  protected InstanceKey[] getInvariantContents(SymbolTable symbolTable, DefUse du, CGNode node, int valueNumber, HeapModel hm) {
    return getInvariantContents(symbolTable, du, node, valueNumber, hm, false);
  }

  protected InstanceKey[] getInvariantContents(SymbolTable symbolTable, DefUse du, CGNode node, int valueNumber, HeapModel hm,
      boolean ensureIndexes) {
    InstanceKey[] result;
    if (isConstantRef(symbolTable, valueNumber)) {
      Object x = symbolTable.getConstantValue(valueNumber);
      if (x instanceof String) {
        // this is always the case in Java. use strong typing in the call to getInstanceKeyForConstant.
        String S = (String) x;
        TypeReference type = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getConstantType(S);
        if (type == null) {
          return new InstanceKey[0];
        }
        InstanceKey ik = hm.getInstanceKeyForConstant(type, S);
        if (ik != null) {
          result = new InstanceKey[] { ik };
        } else {
          result = new InstanceKey[0];
        }
      } else {
        // some non-built in type (e.g. Integer). give up on strong typing.
        // language-specific subclasses (e.g. Javascript) should override this method to get strong typing
        // with generics if desired.
        TypeReference type = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getConstantType(x);
        if (type == null) {
          return new InstanceKey[0];
        }
        InstanceKey ik = hm.getInstanceKeyForConstant(type, x);
        if (ik != null) {
          result = new InstanceKey[] { ik };
        } else {
          result = new InstanceKey[0];
        }
      }
    } else {
      SSANewInstruction def = (SSANewInstruction) du.getDef(valueNumber);
      InstanceKey iKey = hm.getInstanceKeyForAllocation(node, def.getNewSite());
      result = (iKey == null) ? new InstanceKey[0] : new InstanceKey[] { iKey };
    }

    if (ensureIndexes) {
      for (int i = 0; i < result.length; i++) {
        system.findOrCreateIndexForInstanceKey(result[i]);
      }
    }

    return result;
  }

 * 
  protected boolean isConstantRef(SymbolTable symbolTable, int valueNumber) {
    if (valueNumber == -1) {
      return false;
    }
    if (symbolTable.isConstant(valueNumber)) {
      Object v = symbolTable.getConstantValue(valueNumber);
      return (!(v instanceof Number));
    } else {
      return false;
    }
  }

  /**
   * @author sfink
   * 
   *         A warning for when we fail to resolve the type for a checkcast
   */
  private static class CheckcastFailure extends Warning {

    final TypeReference type;

    CheckcastFailure(TypeReference type) {
      super(Warning.SEVERE);
      this.type = type;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + type;
    }

    public static CheckcastFailure create(TypeReference type) {
      return new CheckcastFailure(type);
    }
  }

  /**
   * @author sfink
   * 
   *         A warning for when we fail to resolve the type for a field
   */
  private static class FieldResolutionFailure extends Warning {

    final FieldReference field;

    FieldResolutionFailure(FieldReference field) {
      super(Warning.SEVERE);
      this.field = field;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + field;
    }

    public static FieldResolutionFailure create(FieldReference field) {
      return new FieldResolutionFailure(field);
    }
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.HeapModel#iteratePointerKeys()
   */
  public Iterator iteratePointerKeys() {
    return system.iteratePointerKeys();
  }

  public static Set getCaughtExceptionTypes(SSAGetCaughtExceptionInstruction instruction, IR ir) {
    if (ir == null) {
      throw new IllegalArgumentException("ir is null");
    }
    if (instruction == null) {
      throw new IllegalArgumentException("instruction is null");
    }
    Iterator exceptionTypes = ((ExceptionHandlerBasicBlock) ir.getControlFlowGraph().getNode(
        instruction.getBasicBlockNumber())).getCaughtExceptionTypes();
    HashSet types = HashSetFactory.make(10);
    for (; exceptionTypes.hasNext();) {
      IClass c = ir.getMethod().getClassHierarchy().lookupClass(exceptionTypes.next());
      if (c != null) {
        types.add(c);
      }
    }
    return types;
  }

  public InstanceKey getInstanceKeyForPEI(CGNode node, ProgramCounter instr, TypeReference type) {
    return getInstanceKeyForPEI(node, instr, type, instanceKeyFactory);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder#makeSolver()
   */
  @Override
  protected IPointsToSolver makeSolver() {
    return new StandardSolver(system, this);
    // return usePreTransitiveSolver ? (IPointsToSolver) new PreTransitiveSolver(system, this) : new StandardSolver(system, this);
    // return true ? (IPointsToSolver)new PreTransitiveSolver(system,this) : new
    // StandardSolver(system,this);
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.propagation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.ibm.wala.analysis.reflection.CloneInterpreter;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.ProgramCounter;
import com.ibm.wala.fixpoint.AbstractOperator;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.ContextKey;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.impl.FakeRootMethod;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.ConditionalBranchInstruction;
    return true;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAAbstractThrowInstruction;
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSACFG;
import com.ibm.wala.ssa.SSACFG.BasicBlock;
import com.ibm.wala.ssa.SSACFG.ExceptionHandlerBasicBlock;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAInstanceofInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.CancelRuntimeException;
import com.ibm.wala.util.MonitorUtil;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.functions.VoidFunction;
import com.ibm.wala.util.intset.IntIterator;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetAction;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.ref.ReferenceCleanser;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * This abstract base class provides the general algorithm for a call graph builder that relies on propagation through an iterative
 * dataflow solver, and constraints generated by statements in SSA form.
 * TODO: This implementation currently keeps all points to sets live ... even those for local variables that do not span
 * interprocedural boundaries. This may be too space-inefficient .. we can consider recomputing local sets on demand.
 */
public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGraphBuilder implements HeapModel {
  private final static boolean DEBUG = false;

  private final static boolean DEBUG_MULTINEWARRAY = DEBUG | false;

  /**
   * Should we periodically clear out soft reference caches in an attempt to help the GC?
   */
  public final static boolean PERIODIC_WIPE_SOFT_CACHES = true;

  /**
   * Interval which defines the period to clear soft reference caches
   */
  public final static int WIPE_SOFT_CACHE_INTERVAL = 2500;

  /**
   * Counter for wiping soft caches
   */
  private static int wipeCount = 0;

  /**
   * use type inference to avoid unnecessary filter constraints?
   */
  // private final static boolean OPTIMIZE_WITH_TYPE_INFERENCE = true;
  /**
   * An optimization: if we can locally determine the final solution for a points-to set, then don't actually create the points-to
   * set, but instead short circuit by propagating the final solution to all such uses.
   * 
   * String constants are ALWAYS considered invariant, regardless of the value of this flag.
   * 
   * However, if this flag is set, then the solver is more aggressive identifying invariants.
   * 
   * Doesn't play well with pre-transitive solver; turning off for now.
   */
  private final static boolean SHORT_CIRCUIT_INVARIANT_SETS = true;

  /**
   * An optimization: if we can locally determine that a particular pointer p has exactly one use, then we don't actually create the
   * points-to-set for p, but instead short-circuit by propagating the final solution to the unique use.
   * 
   * Doesn't play well with pre-transitive solver; turning off for now.
   */
  protected final static boolean SHORT_CIRCUIT_SINGLE_USES = true;

  /**
   * Should we change calls to clone() to assignments?
   */
  private final boolean clone2Assign = false;

  /**
   * Cache for efficiency
   */
  private final static Selector cloneSelector = CloneInterpreter.CLONE.getSelector();

  /**
   * set of class whose clinits have already been processed
   */
  private final Set clinitVisited = HashSetFactory.make();

  private IProgressMonitor monitor;

  protected SSAPropagationCallGraphBuilder(IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache,
      PointerKeyFactory pointerKeyFactory) {
    super(cha, options, cache, pointerKeyFactory);
    // this.usePreTransitiveSolver = options.usePreTransitiveSolver();
  }

  public SSAContextInterpreter getCFAContextInterpreter() {
    return (SSAContextInterpreter) getContextInterpreter();
  }

  /**
   * @param node
   * @param x
   * @param type
   * @return the instance key that represents the exception of type _type_ thrown by a particular PEI.
   * @throws IllegalArgumentException if ikFactory is null
   */
  public static InstanceKey getInstanceKeyForPEI(CGNode node, ProgramCounter x, TypeReference type, InstanceKeyFactory ikFactory) {
    if (ikFactory == null) {
      throw new IllegalArgumentException("ikFactory is null");
    }
    return ikFactory.getInstanceKeyForPEI(node, x, type);
  }

  /**
   * Visit all instructions in a node, and add dataflow constraints induced by each statement in the SSA form.
   * @throws CancelException 
   * 
   * @see com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder#addConstraintsFromNode(com.ibm.wala.ipa.callgraph.CGNode)
   */
  @Override
  protected boolean addConstraintsFromNode(CGNode node, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    if (haveAlreadyVisited(node)) {
      return false;
    } else {
      markAlreadyVisited(node);
    }
    return unconditionallyAddConstraintsFromNode(node, monitor);
  }

  @Override
  protected boolean unconditionallyAddConstraintsFromNode(CGNode node, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    if (PERIODIC_WIPE_SOFT_CACHES) {
      wipeCount++;
      if (wipeCount >= WIPE_SOFT_CACHE_INTERVAL) {
        wipeCount = 0;
        ReferenceCleanser.clearSoftCaches();
      }
    }

    if (DEBUG) {
      System.err.println("\n\nAdd constraints from node " + node);
    }
    IR ir = getCFAContextInterpreter().getIR(node);
    if (DEBUG) {
      if (ir == null) {
        System.err.println("\n   No statements\n");
      } else {
        try {
          System.err.println(ir.toString());
        } catch (Error e) {
          e.printStackTrace();
        }
      }
    }

    if (ir == null) {
      return false;
    }

    addNodeInstructionConstraints(node, monitor);

    DefUse du = getCFAContextInterpreter().getDU(node);
    addNodePassthruExceptionConstraints(node, ir, du);
    // conservatively assume something changed
  }

  /**
   * @return a visitor to examine instructions in the ir
   */
  protected ConstraintVisitor makeVisitor(CGNode node) {
    return new ConstraintVisitor(this, node);
  }

  /**
   * Add pointer flow constraints based on instructions in a given node
   * @throws CancelException 
   */
  protected void addNodeInstructionConstraints(CGNode node, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    ConstraintVisitor v = makeVisitor(node);

    IR ir = v.ir;
    ControlFlowGraph cfg = ir.getControlFlowGraph();
    for (Iterator x = cfg.iterator(); x.hasNext();) {
      BasicBlock b = (BasicBlock) x.next();
      addBlockInstructionConstraints(node, cfg, b, v, monitor);
      if (wasChanged(node)) {
        return;
      }
    }
  }

  /**
   * Add constraints for a particular basic block.
   * @throws CancelException 
   */
  protected void addBlockInstructionConstraints(CGNode node, ControlFlowGraph cfg, BasicBlock b,
      ConstraintVisitor v, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    v.setBasicBlock(b);

    // visit each instruction in the basic block.
    for (Iterator it = b.iterator(); it.hasNext();) {
      MonitorUtil.throwExceptionIfCanceled(monitor);      
      SSAInstruction s = it.next();
      if (s != null) {
        s.visit(v);
        if (wasChanged(node)) {
          return;
        }
      }
    }

    addPhiConstraints(node, cfg, b, v);
  }

  private void addPhiConstraints(CGNode node, ControlFlowGraph cfg, BasicBlock b,
      ConstraintVisitor v) {
    // visit each phi instruction in each successor block
    for (Iterator sbs = cfg.getSuccNodes(b); sbs.hasNext();) {
      BasicBlock sb = (BasicBlock) sbs.next();
      if (!sb.hasPhi()) {
        continue;
      }
      int n = 0;
      for (Iterator back = cfg.getPredNodes(sb); back.hasNext(); n++) {
        if (back.next() == b) {
          break;
        }
      }
      assert n < cfg.getPredNodeCount(sb);
      for (Iterator phis = sb.iteratePhis(); phis.hasNext();) {
        SSAPhiInstruction phi = (SSAPhiInstruction) phis.next();
        if (phi == null) {
          continue;
        }
        PointerKey def = getPointerKeyForLocal(node, phi.getDef());
        if (hasNoInterestingUses(node, phi.getDef(), v.du)) {
          system.recordImplicitPointsToSet(def);
        } else {
          // the following test restricts the constraints to reachable
          // paths, according to verification constraints
          if (phi.getUse(n) > 0) {
            PointerKey use = getPointerKeyForLocal(node, phi.getUse(n));
            if (contentsAreInvariant(v.symbolTable, v.du, phi.getUse(n))) {
              system.recordImplicitPointsToSet(use);
              InstanceKey[] ik = getInvariantContents(v.symbolTable, v.du, node, phi.getUse(n), this);
              for (int i = 0; i < ik.length; i++) {
                system.newConstraint(def, ik[i]);
              }
            } else {
              system.newConstraint(def, assignOperator, use);
            }
          }
        }
      }
    }
  }

  /**
   * Add constraints to represent the flow of exceptions to the exceptional return value for this node
   * 
   * @param node
   * @param ir
   */
  protected void addNodePassthruExceptionConstraints(CGNode node, IR ir, DefUse du) {
    // add constraints relating to thrown exceptions that reach the exit block.
    List peis = getIncomingPEIs(ir, ir.getExitBlock());
    PointerKey exception = getPointerKeyForExceptionalReturnValue(node);

    TypeReference throwableType = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getThrowableType();
    IClass c = node.getClassHierarchy().lookupClass(throwableType);
    addExceptionDefConstraints(ir, du, node, peis, exception, Collections.singleton(c));
  }

  /**
   * Generate constraints which assign exception values into an exception pointer
   * 
   * @param node governing node
   * @param peis list of PEI instructions
   * @param exceptionVar PointerKey representing a pointer to an exception value
   * @param catchClasses the types "caught" by the exceptionVar
   */
  private void addExceptionDefConstraints(IR ir, DefUse du, CGNode node, List peis, PointerKey exceptionVar,
      Set catchClasses) {
    if (DEBUG) {
      System.err.println("Add exception def constraints for node " + node);
    }
    for (Iterator it = peis.iterator(); it.hasNext();) {
      ProgramCounter peiLoc = it.next();
      if (DEBUG) {
        System.err.println("peiLoc: " + peiLoc);
      }
      SSAInstruction pei = ir.getPEI(peiLoc);

      if (DEBUG) {
        System.err.println("Add exceptions from pei " + pei);
      }

      if (pei instanceof SSAAbstractInvokeInstruction) {
        SSAAbstractInvokeInstruction s = (SSAAbstractInvokeInstruction) pei;
        PointerKey e = getPointerKeyForLocal(node, s.getException());

        if (!SHORT_CIRCUIT_SINGLE_USES || !hasUniqueCatchBlock(s, ir)) {
          addAssignmentsForCatchPointerKey(exceptionVar, catchClasses, e);
        }// else {
        // System.err.println("SKIPPING ASSIGNMENTS TO " + exceptionVar + " FROM " +
        // e);
        // }
      } else if (pei instanceof SSAAbstractThrowInstruction) {
        SSAAbstractThrowInstruction s = (SSAAbstractThrowInstruction) pei;
        PointerKey e = getPointerKeyForLocal(node, s.getException());

              break;
        if (contentsAreInvariant(ir.getSymbolTable(), du, s.getException())) {
          InstanceKey[] ik = getInvariantContents(ir.getSymbolTable(), du, node, s.getException(), this);
          for (int i = 0; i < ik.length; i++) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            assignInstanceToCatch(exceptionVar, catchClasses, ik[i]);
          }
        } else {
          addAssignmentsForCatchPointerKey(exceptionVar, catchClasses, e);
        }
      }

      // Account for those exceptions for which we do not actually have a
      // points-to set for
      // the pei, but just instance keys
      Collection types = pei.getExceptionTypes();
      if (types != null) {
        for (Iterator it2 = types.iterator(); it2.hasNext();) {
          TypeReference type = it2.next();
          if (type != null) {
            InstanceKey ik = getInstanceKeyForPEI(node, peiLoc, type, instanceKeyFactory);
            if (ik == null) {
              continue;
            }
            assert ik instanceof ConcreteTypeKey : "uh oh: need to implement getCaughtException constraints for instance " + ik;
            ConcreteTypeKey ck = (ConcreteTypeKey) ik;
            IClass klass = ck.getType();
            if (PropagationCallGraphBuilder.catches(catchClasses, klass, cha)) {
              system.newConstraint(exceptionVar, getInstanceKeyForPEI(node, peiLoc, type, instanceKeyFactory));
            }
          }
        }
      }
    }
  }

  /**
   * @return true iff there's a unique catch block which catches all exceptions thrown by a certain call site.
   */
    protected final IR ir;
  protected static boolean hasUniqueCatchBlock(SSAAbstractInvokeInstruction call, IR ir) {
    ISSABasicBlock[] bb = ir.getBasicBlocksForCall(call.getCallSite());
    if (bb.length == 1) {
      Iterator it = ir.getControlFlowGraph().getExceptionalSuccessors(bb[0]).iterator();
      // check that there's exactly one element in the iterator
      if (it.hasNext()) {
        it.next();
        return (!it.hasNext());
      }
    }
    return false;
  }

  /**
   * precondition: hasUniqueCatchBlock(call,node,cg)
   * 
   * @return the unique pointer key which catches the exceptions thrown by a call
   * @throws IllegalArgumentException if ir == null
   * @throws IllegalArgumentException if call == null
   */
  public PointerKey getUniqueCatchKey(SSAAbstractInvokeInstruction call, IR ir, CGNode node) throws IllegalArgumentException,
      IllegalArgumentException {
    if (call == null) {
      throw new IllegalArgumentException("call == null");
    }
    if (ir == null) {
      throw new IllegalArgumentException("ir == null");
    }
    ISSABasicBlock[] bb = ir.getBasicBlocksForCall(call.getCallSite());
    assert bb.length == 1;
    SSACFG.BasicBlock cb = (BasicBlock) ir.getControlFlowGraph().getExceptionalSuccessors(bb[0]).iterator().next();
    if (cb.isExitBlock()) {
      return getPointerKeyForExceptionalReturnValue(node);
    } else {
      SSACFG.ExceptionHandlerBasicBlock ehbb = (ExceptionHandlerBasicBlock) cb;
      SSAGetCaughtExceptionInstruction ci = ehbb.getCatchInstruction();
      return getPointerKeyForLocal(node, ci.getDef());
    }
  }

  /**
   * @return a List of Instructions that may transfer control to bb via an exceptional edge
   * @throws IllegalArgumentException if ir is null
   */
  public static List getIncomingPEIs(IR ir, ISSABasicBlock bb) {
    if (ir == null) {
      throw new IllegalArgumentException("ir is null");
    }
    if (DEBUG) {
      System.err.println("getIncomingPEIs " + bb);
    }
    ControlFlowGraph g = ir.getControlFlowGraph();
    List result = new ArrayList(g.getPredNodeCount(bb));
    for (Iterator it = g.getPredNodes(bb); it.hasNext();) {
      BasicBlock pred = (BasicBlock) it.next();
      if (DEBUG) {
        System.err.println("pred: " + pred);
      }
      if (pred.isEntryBlock())
        continue;
      int index = pred.getLastInstructionIndex();
      SSAInstruction pei = ir.getInstructions()[index];
      // Note: pei might be null if pred is unreachable.
      // TODO: consider pruning CFG for unreachable blocks.
      if (pei != null && pei.isPEI()) {
        if (DEBUG) {
          System.err.println("PEI: " + pei + " index " + index + " PC " + g.getProgramCounter(index));
        }
        result.add(new ProgramCounter(g.getProgramCounter(index)));
      }
    }
    return result;
  }

  /**
   * A visitor that generates constraints based on statements in SSA form.
   */
  protected static class ConstraintVisitor extends SSAInstruction.Visitor {

    /**
     * The governing call graph builder. This field is used instead of an inner class in order to allow more flexible reuse of this
     * visitor in subclasses
     */
    protected final SSAPropagationCallGraphBuilder builder;

    /**
     * The node whose statements we are currently traversing
     */
    protected final CGNode node;

    /**
     * The governing call graph.
     */
    private final ExplicitCallGraph callGraph;

    /**
     * The governing IR
     */

    /**
     * The governing propagation system, into which constraints are added
     */
    protected final PropagationSystem system;

    /**
     * The basic block currently being processed
     */
    protected ISSABasicBlock basicBlock;

    /**
     * Governing symbol table
     */
    protected final SymbolTable symbolTable;

    /**
     * Def-use information
     */
    protected final DefUse du;

    public ConstraintVisitor(SSAPropagationCallGraphBuilder builder, CGNode node) {
      this.builder = builder;
      this.node = node;

      this.callGraph = builder.getCallGraph();

      this.system = builder.getPropagationSystem();

      this.ir = builder.getCFAContextInterpreter().getIR(node);
      this.symbolTable = this.ir.getSymbolTable();

      this.du = builder.getCFAContextInterpreter().getDU(node);

      assert symbolTable != null;
    }

    protected SSAPropagationCallGraphBuilder getBuilder() {
      return builder;
    }

    protected AnalysisOptions getOptions() {
      return builder.options;
    }

    protected AnalysisCache getAnalysisCache() {
      return builder.getAnalysisCache();
    }

    public PointerKey getPointerKeyForLocal(int valueNumber) {
      return getBuilder().getPointerKeyForLocal(node, valueNumber);
    }

    public FilteredPointerKey getFilteredPointerKeyForLocal(int valueNumber, FilteredPointerKey.TypeFilter filter) {
      return getBuilder().getFilteredPointerKeyForLocal(node, valueNumber, filter);
    }

    public PointerKey getPointerKeyForReturnValue() {
      return getBuilder().getPointerKeyForReturnValue(node);
    }

    public PointerKey getPointerKeyForExceptionalReturnValue() {
      return getBuilder().getPointerKeyForExceptionalReturnValue(node);
    }

    public PointerKey getPointerKeyForStaticField(IField f) {
      return getBuilder().getPointerKeyForStaticField(f);
    }

    public PointerKey getPointerKeyForInstanceField(InstanceKey I, IField f) {
      return getBuilder().getPointerKeyForInstanceField(I, f);
    }

    public PointerKey getPointerKeyForArrayContents(InstanceKey I) {
      return getBuilder().getPointerKeyForArrayContents(I);
    }

    public InstanceKey getInstanceKeyForAllocation(NewSiteReference allocation) {
      return getBuilder().getInstanceKeyForAllocation(node, allocation);
    }

    public InstanceKey getInstanceKeyForMultiNewArray(NewSiteReference allocation, int dim) {
      return getBuilder().getInstanceKeyForMultiNewArray(node, allocation, dim);
    }

    public  InstanceKey getInstanceKeyForConstant(T S) {
      TypeReference type = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getConstantType(S);
      return getBuilder().getInstanceKeyForConstant(type, S);
    }

    public InstanceKey getInstanceKeyForPEI(ProgramCounter instr, TypeReference type) {
      return getBuilder().getInstanceKeyForPEI(node, instr, type);
    }

    public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
      return getBuilder().getInstanceKeyForClassObject(type);
    }

    public CGNode getTargetForCall(CGNode caller, CallSiteReference site, IClass recv, InstanceKey iKey[]) {
      return getBuilder().getTargetForCall(caller, site, recv, iKey);
    }

    protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumber) {
      return getBuilder().contentsAreInvariant(symbolTable, du, valueNumber);
    }

    protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumber[]) {
      return getBuilder().contentsAreInvariant(symbolTable, du, valueNumber);
    }

    protected InstanceKey[] getInvariantContents(int valueNumber) {
      return getInvariantContents(ir.getSymbolTable(), du, node, valueNumber);
    }

    protected InstanceKey[] getInvariantContents(SymbolTable symbolTable, DefUse du, CGNode node, int valueNumber) {
      return getBuilder().getInvariantContents(symbolTable, du, node, valueNumber, getBuilder());
    }

    protected IClassHierarchy getClassHierarchy() {
      return getBuilder().getClassHierarchy();
    }

    protected boolean hasNoInterestingUses(int vn) {
      return getBuilder().hasNoInterestingUses(node, vn, du);
    }

    protected boolean isRootType(IClass klass) {
      return getBuilder().isRootType(klass);
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitArrayLoad(com.ibm.wala.ssa.SSAArrayLoadInstruction)
     */
    @Override
    public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
      // skip arrays of primitive type
      if (instruction.typeIsPrimitive()) {
        return;
      }
      doVisitArrayLoad(instruction.getDef(), instruction.getArrayRef());
    }

    protected void doVisitArrayLoad(int def, int arrayRef) {
      PointerKey result = getPointerKeyForLocal(def);
      PointerKey arrayRefPtrKey = getPointerKeyForLocal(arrayRef);
      if (hasNoInterestingUses(def)) {
        system.recordImplicitPointsToSet(result);
      } else {
        if (contentsAreInvariant(symbolTable, du, arrayRef)) {
          system.recordImplicitPointsToSet(arrayRefPtrKey);
          InstanceKey[] ik = getInvariantContents(arrayRef);
          for (int i = 0; i < ik.length; i++) {
            if (!representsNullType(ik[i])) {
              system.findOrCreateIndexForInstanceKey(ik[i]);
              PointerKey p = getPointerKeyForArrayContents(ik[i]);
              if (p == null) {
              } else {
                system.newConstraint(result, assignOperator, p);
              }
            }
          }
        } else {
          assert !system.isUnified(result);
          assert !system.isUnified(arrayRefPtrKey);
          system.newSideEffect(getBuilder().new ArrayLoadOperator(system.findOrCreatePointsToSet(result)), arrayRefPtrKey);
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitArrayStore(com.ibm.wala.ssa.SSAArrayStoreInstruction)
     */
    public void doVisitArrayStore(int arrayRef, int value) {
      // (requires the creation of assign constraints as
      // the set points-to(a[]) grows.)
      PointerKey valuePtrKey = getPointerKeyForLocal(value);
      PointerKey arrayRefPtrKey = getPointerKeyForLocal(arrayRef);
      // if (!supportFullPointerFlowGraph &&
      // contentsAreInvariant(instruction.getArrayRef())) {
      if (contentsAreInvariant(symbolTable, du, arrayRef)) {
        system.recordImplicitPointsToSet(arrayRefPtrKey);
        InstanceKey[] ik = getInvariantContents(arrayRef);

        for (int i = 0; i < ik.length; i++) {
          if (!representsNullType(ik[i]) && !(ik[i] instanceof ZeroLengthArrayInNode)) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            PointerKey p = getPointerKeyForArrayContents(ik[i]);
            IClass contents = ((ArrayClass) ik[i].getConcreteType()).getElementClass();
            if (p == null) {
            } else {
              if (contentsAreInvariant(symbolTable, du, value)) {
                system.recordImplicitPointsToSet(valuePtrKey);
        } else {
                InstanceKey[] vk = getInvariantContents(value);
                for (int j = 0; j < vk.length; j++) {
                  system.findOrCreateIndexForInstanceKey(vk[j]);
                  if (vk[j].getConcreteType() != null) {
                    if (getClassHierarchy().isAssignableFrom(contents, vk[j].getConcreteType())) {
                      system.newConstraint(p, vk[j]);
                    }
                  }
                }
              } else {
                if (isRootType(contents)) {
                  system.newConstraint(p, assignOperator, valuePtrKey);
                } else {
                  system.newConstraint(p, getBuilder().filterOperator, valuePtrKey);
                }
              }
            }
          }
        }
      } else {
        if (contentsAreInvariant(symbolTable, du, value)) {
          system.recordImplicitPointsToSet(valuePtrKey);
          InstanceKey[] ik = getInvariantContents(value);
          for (int i = 0; i < ik.length; i++) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            assert !system.isUnified(arrayRefPtrKey);
            system.newSideEffect(getBuilder().new InstanceArrayStoreOperator(ik[i]), arrayRefPtrKey);
          }
        } else {
          system.newSideEffect(getBuilder().new ArrayStoreOperator(system.findOrCreatePointsToSet(valuePtrKey)), arrayRefPtrKey);
        }
      }
    }

    @Override
    public void visitArrayStore(SSAArrayStoreInstruction instruction) {
      // skip arrays of primitive type
      if (instruction.typeIsPrimitive()) {
        return;
      }
      doVisitArrayStore(instruction.getArrayRef(), instruction.getValue());
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitCheckCast(com.ibm.wala.ssa.SSACheckCastInstruction)
     */
    @Override
    public void visitCheckCast(SSACheckCastInstruction instruction) {
 
      boolean isRoot = false;
    Set types = HashSetFactory.make();
      
      for(TypeReference t : instruction.getDeclaredResultTypes()) {
        IClass cls = getClassHierarchy().lookupClass(t);
        if (cls == null) {
          Warnings.add(CheckcastFailure.create(t));
         return;
        } else {
          if (isRootType(cls)) {
            isRoot = true;
          }
          types.add(cls);
        }
      }
      
      PointerKey result = getFilteredPointerKeyForLocal(instruction.getResult(), new FilteredPointerKey.MultipleClassesFilter(types.toArray(new IClass[ types.size() ])));
      PointerKey value = getPointerKeyForLocal(instruction.getVal());

      if (hasNoInterestingUses(instruction.getDef())) {
        system.recordImplicitPointsToSet(result);
      } else {
        if (contentsAreInvariant(symbolTable, du, instruction.getVal())) {
          system.recordImplicitPointsToSet(value);
          InstanceKey[] ik = getInvariantContents(instruction.getVal());
          for(TypeReference t : instruction.getDeclaredResultTypes()) {
            IClass cls = getClassHierarchy().lookupClass(t);

            if (cls.isInterface()) {
              for (int i = 0; i < ik.length; i++) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                if (getClassHierarchy().implementsInterface(ik[i].getConcreteType(), cls)) {
                  system.newConstraint(result, ik[i]);
                }
              }
            } else {
              for (int i = 0; i < ik.length; i++) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                if (getClassHierarchy().isSubclassOf(ik[i].getConcreteType(), cls)) {
                  system.newConstraint(result, ik[i]);
                }
              }
            }
          }
          if (isRoot) {
            system.newConstraint(result, assignOperator, value);
          } else {
            system.newConstraint(result, getBuilder().filterOperator, value);
          }
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitReturn(com.ibm.wala.ssa.SSAReturnInstruction)
     */
    @Override
    public void visitReturn(SSAReturnInstruction instruction) {

      // skip returns of primitive type
      if (instruction.returnsPrimitiveType() || instruction.returnsVoid()) {
        return;
      }
      if (DEBUG) {
        System.err.println("visitReturn: " + instruction);
      }

      PointerKey returnValue = getPointerKeyForReturnValue();
      PointerKey result = getPointerKeyForLocal(instruction.getResult());
      if (contentsAreInvariant(symbolTable, du, instruction.getResult())) {
        system.recordImplicitPointsToSet(result);
        InstanceKey[] ik = getInvariantContents(instruction.getResult());
        for (int i = 0; i < ik.length; i++) {
          if (DEBUG) {
            System.err.println("invariant contents: " + returnValue + " " + ik[i]);
          }
          system.newConstraint(returnValue, ik[i]);
        }
      } else {
        system.newConstraint(returnValue, assignOperator, result);
      }
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitGet(com.ibm.wala.ssa.SSAGetInstruction)
     */
    @Override
    public void visitGet(SSAGetInstruction instruction) {
      visitGetInternal(instruction.getDef(), instruction.getRef(), instruction.isStatic(), instruction.getDeclaredField());
    }

    protected void visitGetInternal(int lval, int ref, boolean isStatic, FieldReference field) {
      if (DEBUG) {
        System.err.println("visitGet " + field);
      }

      // skip getfields of primitive type (optimisation)
      if (field.getFieldType().isPrimitiveType()) {
        return;
      }
      PointerKey def = getPointerKeyForLocal(lval);
      assert def != null;

      IField f = getClassHierarchy().resolveField(field);
      if (f == null && callGraph.getFakeRootNode().getMethod().getDeclaringClass().getReference().equals(field.getDeclaringClass())) {
        f = callGraph.getFakeRootNode().getMethod().getDeclaringClass().getField(field.getName());
      }

      if (f == null) {
        return;
      }

      if (hasNoInterestingUses(lval)) {
        system.recordImplicitPointsToSet(def);
      } else {
        if (isStatic) {
          PointerKey fKey = getPointerKeyForStaticField(f);
          system.newConstraint(def, assignOperator, fKey);
          IClass klass = getClassHierarchy().lookupClass(field.getDeclaringClass());
          if (klass == null) {
          } else {
            // side effect of getstatic: may call class initializer
            if (DEBUG) {
              System.err.println("getstatic call class init " + klass);
            }
            processClassInitializer(klass);
          }
        } else {
          PointerKey refKey = getPointerKeyForLocal(ref);
          // if (!supportFullPointerFlowGraph &&
          // contentsAreInvariant(ref)) {
          if (contentsAreInvariant(symbolTable, du, ref)) {
            system.recordImplicitPointsToSet(refKey);
            InstanceKey[] ik = getInvariantContents(ref);
            for (int i = 0; i < ik.length; i++) {
              if (!representsNullType(ik[i])) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                PointerKey p = getPointerKeyForInstanceField(ik[i], f);
                system.newConstraint(def, assignOperator, p);
              }
            }
          } else {
            system.newSideEffect(getBuilder().new GetFieldOperator(f, system.findOrCreatePointsToSet(def)), refKey);
          }
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitPut(com.ibm.wala.ssa.PutInstruction)
     */
    @Override
    public void visitPut(SSAPutInstruction instruction) {
      visitPutInternal(instruction.getVal(), instruction.getRef(), instruction.isStatic(), instruction.getDeclaredField());
    }

    public void visitPutInternal(int rval, int ref, boolean isStatic, FieldReference field) {

      if (DEBUG) {
        System.err.println("visitPut " + field);
      }

      // skip putfields of primitive type
      if (field.getFieldType().isPrimitiveType()) {
        return;
      }
      IField f = getClassHierarchy().resolveField(field);
      if (f == null) {
        if (DEBUG) {
          System.err.println("Could not resolve field " + field);
        }
        Warnings.add(FieldResolutionFailure.create(field));
        return;
      }
      assert isStatic || !symbolTable.isStringConstant(ref) : "put to string constant shouldn't be allowed?";
      if (isStatic) {
        processPutStatic(rval, field, f);
      } else {
        processPutField(rval, ref, f);
      }
    }

    private void processPutField(int rval, int ref, IField f) {
      assert !f.getFieldTypeReference().isPrimitiveType();
      PointerKey refKey = getPointerKeyForLocal(ref);
      PointerKey rvalKey = getPointerKeyForLocal(rval);
      // if (!supportFullPointerFlowGraph &&
      // contentsAreInvariant(rval)) {
      if (contentsAreInvariant(symbolTable, du, rval)) {
        system.recordImplicitPointsToSet(rvalKey);
        InstanceKey[] ik = getInvariantContents(rval);
        if (contentsAreInvariant(symbolTable, du, ref)) {
          system.recordImplicitPointsToSet(refKey);
          InstanceKey[] refk = getInvariantContents(ref);
          for (int j = 0; j < refk.length; j++) {
            }
            if (!representsNullType(refk[j])) {
              system.findOrCreateIndexForInstanceKey(refk[j]);
              PointerKey p = getPointerKeyForInstanceField(refk[j], f);
              for (int i = 0; i < ik.length; i++) {
                system.newConstraint(p, ik[i]);
              }
            }
          }
        } else {
          for (int i = 0; i < ik.length; i++) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            system.newSideEffect(getBuilder().new InstancePutFieldOperator(f, ik[i]), refKey);
          }
        }
      } else {
        if (contentsAreInvariant(symbolTable, du, ref)) {
          system.recordImplicitPointsToSet(refKey);
          InstanceKey[] refk = getInvariantContents(ref);
          for (int j = 0; j < refk.length; j++) {
            if (!representsNullType(refk[j])) {
              system.findOrCreateIndexForInstanceKey(refk[j]);
              PointerKey p = getPointerKeyForInstanceField(refk[j], f);
              system.newConstraint(p, assignOperator, rvalKey);
            }
          }
        } else {
          if (DEBUG) {
            System.err.println("adding side effect " + f);
          }
          system.newSideEffect(getBuilder().new PutFieldOperator(f, system.findOrCreatePointsToSet(rvalKey)), refKey);
        }
      }
    }

    private void processPutStatic(int rval, FieldReference field, IField f) {
      PointerKey fKey = getPointerKeyForStaticField(f);
      PointerKey rvalKey = getPointerKeyForLocal(rval);

      // if (!supportFullPointerFlowGraph &&
      // contentsAreInvariant(rval)) {
      if (contentsAreInvariant(symbolTable, du, rval)) {
        system.recordImplicitPointsToSet(rvalKey);
        InstanceKey[] ik = getInvariantContents(rval);
        for (int i = 0; i < ik.length; i++) {
          system.newConstraint(fKey, ik[i]);
        }
      } else {
        system.newConstraint(fKey, assignOperator, rvalKey);
      }
      if (DEBUG) {
        System.err.println("visitPut class init " + field.getDeclaringClass() + " " + field);
      }
      // side effect of putstatic: may call class initializer
      IClass klass = getClassHierarchy().lookupClass(field.getDeclaringClass());
      if (klass == null) {
        Warnings.add(FieldResolutionFailure.create(field));
      } else {
        processClassInitializer(klass);
      }
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitInvoke(com.ibm.wala.ssa.InvokeInstruction)
     */
    @Override
    public void visitInvoke(SSAInvokeInstruction instruction) {
      visitInvokeInternal(instruction, new DefaultInvariantComputer());
    }

    protected void visitInvokeInternal(final SSAAbstractInvokeInstruction instruction, InvariantComputer invs) {
      if (DEBUG) {
        System.err.println("visitInvoke: " + instruction);
      }

      PointerKey uniqueCatch = null;
      if (hasUniqueCatchBlock(instruction, ir)) {
        uniqueCatch = getBuilder().getUniqueCatchKey(instruction, ir, node);
      }

      InstanceKey[][] invariantParameters = invs.computeInvariantParameters(instruction);
      if (instruction.getCallSite().isStatic()) {
        for (CGNode n : getBuilder().getTargetsForCall(node, instruction, invariantParameters)) {
          getBuilder().processResolvedCall(node, instruction, n, invariantParameters, uniqueCatch);
          if (DEBUG) {
            System.err.println("visitInvoke class init " + n);
          }

          // side effect of invoke: may call class initializer
          processClassInitializer(n.getMethod().getDeclaringClass());
        }
      } else {
        // Add a side effect that will fire when we determine a value
        // for a dispatch parameter. This side effect will create a new node
        // and new constraints based on the new callee context.
        IntSet params = getBuilder().getContextSelector().getRelevantParameters(node, instruction.getCallSite());
        if (! params.contains(0)) {
          params = IntSetUtil.makeMutableCopy(params);
          ((MutableIntSet)params).add(0);
        }
        final int vns[] = new int[ params.size() ];
        params.foreach(new IntSetAction() {
          private int i = 0;
          public void act(int x) {
            vns[i++] = instruction.getUse(x);
          }
        });
        
        if (contentsAreInvariant(symbolTable, du, vns)) {
          for(CGNode n : getBuilder().getTargetsForCall(node, instruction, invariantParameters)) {
              getBuilder().processResolvedCall(node, instruction, n, invariantParameters, uniqueCatch);
              // side effect of invoke: may call class initializer
              processClassInitializer(n.getMethod().getDeclaringClass());
          }
        } else {
          if (DEBUG) {
            System.err.println("Add side effect, dispatch to " + instruction + " for " + params);
          }
 
          final List pks = new ArrayList(params.size());
          params.foreach(new IntSetAction() {
            public void act(int x) {
              if (!contentsAreInvariant(symbolTable, du, instruction.getUse(x))) {
                pks.add(getBuilder().getPointerKeyForLocal(node, instruction.getUse(x)));
              }
            }
          });
   
          DispatchOperator dispatchOperator = getBuilder().new DispatchOperator(instruction, node,
              invariantParameters, uniqueCatch, params);
   */
          system.newSideEffect(dispatchOperator, pks.toArray(new PointerKey[pks.size()]));
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitNew(com.ibm.wala.ssa.NewInstruction)
     */
    @Override
    public void visitNew(SSANewInstruction instruction) {
      InstanceKey iKey = getInstanceKeyForAllocation(instruction.getNewSite());

      if (iKey == null) {
        // something went wrong. I hope someone raised a warning.
        return;
      }
      PointerKey def = getPointerKeyForLocal(instruction.getDef());
      IClass klass = iKey.getConcreteType();

      if (DEBUG) {
        System.err.println("visitNew: " + instruction + " " + iKey + " " + system.findOrCreateIndexForInstanceKey(iKey));
      }

      if (klass == null) {
        if (DEBUG) {
          System.err.println("Resolution failure: " + instruction);
        }
        return;
      }

      if (!contentsAreInvariant(symbolTable, du, instruction.getDef())) {
        system.newConstraint(def, iKey);
      } else {
        system.findOrCreateIndexForInstanceKey(iKey);
        system.recordImplicitPointsToSet(def);
      }

      // side effect of new: may call class initializer
      if (DEBUG) {
        System.err.println("visitNew call clinit: " + klass);
      }
      processClassInitializer(klass);

      // add instance keys and pointer keys for array contents
      int dim = 0;
      InstanceKey lastInstance = iKey;
      while (klass != null && klass.isArrayClass()) {
        klass = ((ArrayClass) klass).getElementClass();
        // klass == null means it's a primitive
        if (klass != null && klass.isArrayClass()) {
          if (instruction.getNumberOfUses() <= (dim + 1)) {
            break;
          }
          int sv = instruction.getUse(dim + 1);
          if (ir.getSymbolTable().isIntegerConstant(sv)) {
            Integer c = (Integer) ir.getSymbolTable().getConstantValue(sv);
            if (c.intValue() == 0) {
          }
          InstanceKey ik = getInstanceKeyForMultiNewArray(instruction.getNewSite(), dim);
          PointerKey pk = getPointerKeyForArrayContents(lastInstance);
          if (DEBUG_MULTINEWARRAY) {
            System.err.println("multinewarray constraint: ");
            System.err.println("   pk: " + pk);
            System.err.println("   ik: " + system.findOrCreateIndexForInstanceKey(ik) + " concrete type " + ik.getConcreteType()
                + " is " + ik);
            System.err.println("   klass:" + klass);
          }
          system.newConstraint(pk, ik);
          lastInstance = ik;
          dim++;
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitThrow(com.ibm.wala.ssa.ThrowInstruction)
     */
    @Override
    public void visitThrow(SSAThrowInstruction instruction) {
      // don't do anything: we handle exceptional edges
      // in a separate pass
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitGetCaughtException(com.ibm.wala.ssa.GetCaughtExceptionInstruction)
     */
    @Override
    public void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction) {
      List peis = getIncomingPEIs(ir, getBasicBlock());
      PointerKey def = getPointerKeyForLocal(instruction.getDef());
      // SJF: we don't optimize based on dead catch blocks yet ... it's a little
      // tricky due interaction with the SINGLE_USE optimization which directly
      // shoves exceptional return values from calls into exception vars.
      // it may not be worth doing this.
      // if (hasNoInterestingUses(instruction.getDef(), du)) {
      // solver.recordImplicitPointsToSet(def);
      // } else {
      Set types = getCaughtExceptionTypes(instruction, ir);
      getBuilder().addExceptionDefConstraints(ir, du, node, peis, def, types);
      // }
    }

    /**
     * TODO: What is this doing? Document me!
     */
    private int booleanConstantTest(SSAConditionalBranchInstruction c, int v) {
      int result = 0;

      // right for OPR_eq
      if ((symbolTable.isZeroOrFalse(c.getUse(0)) && c.getUse(1) == v)
          || (symbolTable.isZeroOrFalse(c.getUse(1)) && c.getUse(0) == v)) {
        result = -1;
      } else if ((symbolTable.isOneOrTrue(c.getUse(0)) && c.getUse(1) == v)
          || (symbolTable.isOneOrTrue(c.getUse(1)) && c.getUse(0) == v)) {
        result = 1;
      }

      if (c.getOperator() == ConditionalBranchInstruction.Operator.NE) {
        result = -result;
      }

      return result;
    }

    private int nullConstantTest(SSAConditionalBranchInstruction c, int v) {
      if ((symbolTable.isNullConstant(c.getUse(0)) && c.getUse(1) == v)
          || (symbolTable.isNullConstant(c.getUse(1)) && c.getUse(0) == v)) {
        if (c.getOperator() == ConditionalBranchInstruction.Operator.EQ) {
          return 1;
        } else {
          return -1;
        }
      } else {
        return 0;
      }
    }

    @Override
    public void visitPhi(SSAPhiInstruction instruction) {
      if (ir.getMethod() instanceof AbstractRootMethod) {
        PointerKey dst = getPointerKeyForLocal(instruction.getDef());
        if (hasNoInterestingUses(instruction.getDef())) {
          system.recordImplicitPointsToSet(dst);
        } else {
          for (int i = 0; i < instruction.getNumberOfUses(); i++) {
            PointerKey use = getPointerKeyForLocal(instruction.getUse(i));
            if (contentsAreInvariant(symbolTable, du, instruction.getUse(i))) {
              system.recordImplicitPointsToSet(use);
              InstanceKey[] ik = getInvariantContents(instruction.getUse(i));
              for (int j = 0; j < ik.length; j++) {
                system.newConstraint(dst, ik[j]);
              }
            } else {
              system.newConstraint(dst, assignOperator, use);
            }
          }
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitPi(com.ibm.wala.ssa.SSAPiInstruction)
     */
    @Override
    public void visitPi(SSAPiInstruction instruction) {
      int dir;

      if (hasNoInterestingUses(instruction.getDef())) {
        PointerKey dst = getPointerKeyForLocal(instruction.getDef());
        system.recordImplicitPointsToSet(dst);
      } else {
        ControlFlowGraph cfg = ir.getControlFlowGraph();
        if (com.ibm.wala.cfg.Util.endsWithConditionalBranch(cfg, getBasicBlock()) && cfg.getSuccNodeCount(getBasicBlock()) == 2) {
          SSAConditionalBranchInstruction cond = (SSAConditionalBranchInstruction) com.ibm.wala.cfg.Util.getLastInstruction(cfg,
              getBasicBlock());
          SSAInstruction cause = instruction.getCause();
          BasicBlock target = (BasicBlock) cfg.getNode(instruction.getSuccessor());

          if ((cause instanceof SSAInstanceofInstruction)) {
            int direction = booleanConstantTest(cond, cause.getDef());
            if (direction != 0) {
              TypeReference type = ((SSAInstanceofInstruction) cause).getCheckedType();
              IClass cls = getClassHierarchy().lookupClass(type);
              if (cls == null) {
                PointerKey dst = getPointerKeyForLocal(instruction.getDef());
                addPiAssignment(dst, instruction.getVal());
              } else {
                PointerKey dst = getFilteredPointerKeyForLocal(instruction.getDef(), new FilteredPointerKey.SingleClassFilter(cls));
                PointerKey src = getPointerKeyForLocal(instruction.getVal());
                if ((target == com.ibm.wala.cfg.Util.getTakenSuccessor(cfg, getBasicBlock()) && direction == 1)
                    || (target == com.ibm.wala.cfg.Util.getNotTakenSuccessor(cfg, getBasicBlock()) && direction == -1)) {
                  system.newConstraint(dst, getBuilder().filterOperator, src);
                } else {
                  system.newConstraint(dst, getBuilder().inverseFilterOperator, src);
                }
              }
            }
          } else if ((dir = nullConstantTest(cond, instruction.getVal())) != 0) {
            if ((target == com.ibm.wala.cfg.Util.getTakenSuccessor(cfg, getBasicBlock()) && dir == -1)
                || (target == com.ibm.wala.cfg.Util.getNotTakenSuccessor(cfg, getBasicBlock()) && dir == 1)) {
              PointerKey dst = getPointerKeyForLocal(instruction.getDef());
              addPiAssignment(dst, instruction.getVal());
            }
          } else {
            PointerKey dst = getPointerKeyForLocal(instruction.getDef());
            addPiAssignment(dst, instruction.getVal());
          }
        } else {
          PointerKey dst = getPointerKeyForLocal(instruction.getDef());
          addPiAssignment(dst, instruction.getVal());
        }
      }
    }

    /**
     * Add a constraint to the system indicating that the contents of local src flows to dst, with no special type filter.
     */
    private void addPiAssignment(PointerKey dst, int src) {
      PointerKey srcKey = getPointerKeyForLocal(src);
      if (contentsAreInvariant(symbolTable, du, src)) {
        system.recordImplicitPointsToSet(srcKey);
        InstanceKey[] ik = getInvariantContents(src);
        for (int j = 0; j < ik.length; j++) {
          system.newConstraint(dst, ik[j]);
        }
      } else {
        system.newConstraint(dst, assignOperator, srcKey);
      }

    }

    public ISSABasicBlock getBasicBlock() {
      return basicBlock;
    }

    /**
     * The calling loop must call this in each iteration!
     */
    public void setBasicBlock(ISSABasicBlock block) {
      basicBlock = block;
    }

    protected interface InvariantComputer {
 
      InstanceKey[][] computeInvariantParameters(SSAAbstractInvokeInstruction call);

    }

    public class DefaultInvariantComputer implements InvariantComputer {
    /**
     * Side effect: records invariant parameters as implicit points-to-sets.
     * 
     * @return if non-null, then result[i] holds the set of instance keys which may be passed as the ith parameter. (which must be
     *         invariant)
     */
    public InstanceKey[][] computeInvariantParameters(SSAAbstractInvokeInstruction call) {
      InstanceKey[][] constParams = null;
      for (int i = 0; i < call.getNumberOfUses(); i++) {
        // not sure how getUse(i) <= 0 .. dead code?
        // TODO: investigate
        if (call.getUse(i) > 0) {
          if (contentsAreInvariant(symbolTable, du, call.getUse(i))) {
            system.recordImplicitPointsToSet(getPointerKeyForLocal(call.getUse(i)));
            if (constParams == null) {
              constParams = new InstanceKey[call.getNumberOfUses()][];
            }
            constParams[i] = getInvariantContents(call.getUse(i));
            for (int j = 0; j < constParams[i].length; j++) {
              system.findOrCreateIndexForInstanceKey(constParams[i][j]);
            }
          }
        }
      }
      return constParams;
    }
    }
    
    @Override
    public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
            }
      PointerKey def = getPointerKeyForLocal(instruction.getDef());
      assert instruction.getType() == TypeReference.JavaLangClass;
      InstanceKey iKey = getInstanceKeyForClassObject((TypeReference) instruction.getToken());
      IClass klass = getClassHierarchy().lookupClass((TypeReference) instruction.getToken());
      if (klass != null) {
        processClassInitializer(klass);
      }

      if (!contentsAreInvariant(symbolTable, du, instruction.getDef())) {
        system.newConstraint(def, iKey);
      } else {
        system.findOrCreateIndexForInstanceKey(iKey);
        system.recordImplicitPointsToSet(def);
      }
    }

    /**
     * TODO: lift most of this logic to PropagationCallGraphBuilder
     * 
     * Add a call to the class initializer from the root method.
     */
    private void processClassInitializer(IClass klass) {

      assert klass != null;

      if (!getBuilder().getOptions().getHandleStaticInit()) {
        return;
      }

      if (getBuilder().clinitVisited.contains(klass)) {
        return;
      }
      getBuilder().clinitVisited.add(klass);

      if (klass.getClassInitializer() != null) {
        if (DEBUG) {
          System.err.println("process class initializer for " + klass);
        }

        // add an invocation from the fake root method to the 
        AbstractRootMethod fakeWorldClinitMethod = (AbstractRootMethod) callGraph.getFakeWorldClinitNode().getMethod();
        MethodReference m = klass.getClassInitializer().getReference();
        CallSiteReference site = CallSiteReference.make(1, m, IInvokeInstruction.Dispatch.STATIC);
        IMethod targetMethod = getOptions().getMethodTargetSelector().getCalleeTarget(callGraph.getFakeRootNode(), site, null);
        if (targetMethod != null) {
          CGNode target = getTargetForCall(callGraph.getFakeRootNode(), site, null, null);
          if (target != null && callGraph.getPredNodeCount(target) == 0) {
            SSAAbstractInvokeInstruction s = fakeWorldClinitMethod.addInvocation(new int[0], site);
            PointerKey uniqueCatch = getBuilder().getPointerKeyForExceptionalReturnValue(callGraph.getFakeRootNode());
            getBuilder().processResolvedCall(callGraph.getFakeWorldClinitNode(), s, target, null, uniqueCatch);
          }
        }
      }

      IClass sc = klass.getSuperclass();
      if (sc != null) {
        processClassInitializer(sc);
      }
    }
  }

  /**
   * Add constraints for a call site after we have computed a reachable target for the dispatch
   * 
   * Side effect: add edge to the call graph.
   * 
   * @param instruction
   * @param constParams if non-null, then constParams[i] holds the set of instance keys that are passed as param i, or null if param
   *          i is not invariant
   * @param uniqueCatchKey if non-null, then this is the unique PointerKey that catches all exceptions from this call site.
   */
  @SuppressWarnings("deprecation")
  private void processResolvedCall(CGNode caller, SSAAbstractInvokeInstruction instruction, CGNode target,
      InstanceKey[][] constParams, PointerKey uniqueCatchKey) {

    if (DEBUG) {
      System.err.println("processResolvedCall: " + caller + " ," + instruction + " , " + target);
    }

    if (DEBUG) {
      System.err.println("addTarget: " + caller + " ," + instruction + " , " + target);
    }
    caller.addTarget(instruction.getCallSite(), target);

    if (FakeRootMethod.isFakeRootMethod(caller.getMethod().getReference())) {
      if (entrypointCallSites.contains(instruction.getCallSite())) {
        callGraph.registerEntrypoint(target);
      }
    }

    if (!haveAlreadyVisited(target)) {
      markDiscovered(target);
    }
    
    processCallingConstraints(caller, instruction, target, constParams, uniqueCatchKey);
  }
  
  protected void processCallingConstraints(CGNode caller, SSAAbstractInvokeInstruction instruction, CGNode target,
      InstanceKey[][] constParams, PointerKey uniqueCatchKey) {
    // TODO: i'd like to enable this optimization, but it's a little tricky
    // to recover the implicit points-to sets with recursion. TODO: don't
    // be lazy and code the recursive logic to enable this.
    // if (hasNoInstructions(target)) {
    // // record points-to sets for formals implicitly .. computed on
    // // demand.
    // // TODO: generalize this by using hasNoInterestingUses on parameters.
    // // however .. have to be careful to cache results in that case ... don't
    // // want
    // // to recompute du each time we process a call to Object. !
    // for (int i = 0; i < instruction.getNumberOfUses(); i++) {
    // // we rely on the invariant that the value number for the ith parameter
    // // is i+1
    // final int vn = i + 1;
    // PointerKey formal = getPointerKeyForLocal(target, vn);
    // if (target.getMethod().getParameterType(i).isReferenceType()) {
    // system.recordImplicitPointsToSet(formal);
    // }
    // }
    // } else {
    // generate contraints from parameter passing
    int nUses = instruction.getNumberOfParameters();
    int nExpected = target.getMethod().getNumberOfParameters();

    /*
     * int nExpected = target.getMethod().getReference().getNumberOfParameters(); if (!target.getMethod().isStatic() &&
     * !target.getMethod().isClinit()) { nExpected++; }
     */

    if (nUses != nExpected) {
      // some sort of unverifiable code mismatch. give up.
      return;
    }

    // we're a little sloppy for now ... we don't filter calls to
    // java.lang.Object.
    // TODO: we need much more precise filters than cones in order to handle
    // the various types of dispatch logic. We need a filter that expresses
    // "the set of types s.t. x.foo resolves to y.foo."
    for (int i = 0; i < instruction.getNumberOfParameters(); i++) {
      if (target.getMethod().getParameterType(i).isReferenceType()) {
        PointerKey formal = getTargetPointerKey(target, i);
        if (constParams != null && constParams[i] != null) {
          InstanceKey[] ik = constParams[i];
          for (int j = 0; j < ik.length; j++) {
            system.newConstraint(formal, ik[j]);
          }
        } else {
          if (instruction.getUse(i) < 0) {
            Assertions.UNREACHABLE("unexpected " + instruction + " in " + caller);
          }
          PointerKey actual = getPointerKeyForLocal(caller, instruction.getUse(i));
          if (formal instanceof FilteredPointerKey) {
            system.newConstraint(formal, filterOperator, actual);
          } else {
            system.newConstraint(formal, assignOperator, actual);
          }
        }
      }
    }

    // generate contraints from return value.
    if (instruction.hasDef() && instruction.getDeclaredResultType().isReferenceType()) {
      PointerKey result = getPointerKeyForLocal(caller, instruction.getDef());
      PointerKey ret = getPointerKeyForReturnValue(target);
      system.newConstraint(result, assignOperator, ret);
    }
    // generate constraints from exception return value.
    PointerKey e = getPointerKeyForLocal(caller, instruction.getException());
    PointerKey er = getPointerKeyForExceptionalReturnValue(target);
    if (SHORT_CIRCUIT_SINGLE_USES && uniqueCatchKey != null) {
      // e has exactly one use. so, represent e implicitly
      system.newConstraint(uniqueCatchKey, assignOperator, er);
    } else {
      system.newConstraint(e, assignOperator, er);
    }
    // }
  }

  /**
   * An operator to fire when we discover a potential new callee for a virtual or interface call site.
   * 
   * This operator will create a new callee context and constraints if necessary.
  final class DispatchOperator extends AbstractOperator implements IPointerOperator {
    private final SSAAbstractInvokeInstruction call;

    private final CGNode node;

    private final InstanceKey[][] constParams;

    private final PointerKey uniqueCatch;

    /**
     * relevant parameter indices for the registered {@link ContextSelector}
     * 
     * @see ContextSelector#getRelevantParameters(CGNode, CallSiteReference)
     */
    private final int[] dispatchIndices;
    
    /**
     * The set of instance keys that have already been processed.
     * previousPtrs[i] contains the processed instance keys for parameter
     * position dispatchIndices[i]
     */
    final private MutableIntSet[] previousPtrs;
    
    /**
     * @param call
     * @param node
     * @param constParams if non-null, then constParams[i] holds the String constant that is passed as param i, or null if param i
     *          is not a String constant
     */
    DispatchOperator(SSAAbstractInvokeInstruction call, CGNode node, InstanceKey[][] constParams,
        PointerKey uniqueCatch, IntSet dispatchIndices) {
      this.call = call;
      this.node = node;
      this.constParams = constParams;
      this.uniqueCatch = uniqueCatch;
      this.dispatchIndices = IntSetUtil.toArray(dispatchIndices);
      // we better always be interested in the receiver
      assert this.dispatchIndices[0] == 0;
      previousPtrs = new MutableIntSet[dispatchIndices.size()];
      for(int i = 0; i < previousPtrs.length; i++) {
        previousPtrs[i] = IntSetUtil.getDefaultIntSetFactory().make();
      }
    }



    /*
     * @see com.ibm.wala.dataflow.fixpoint.UnaryOperator#evaluate(com.ibm.wala.dataflow.fixpoint.IVariable,
     * com.ibm.wala.dataflow.fixpoint.IVariable)
     */
    @Override
    public byte evaluate(PointsToSetVariable lhs, final PointsToSetVariable[] rhs) {
      assert dispatchIndices.length >= rhs.length : "bad operator at " + call;
      // did evaluating the dispatch operation add a new possible target
      // to the call site?  
        DispatchOperator other = (DispatchOperator) o;
      final MutableBoolean addedNewTarget = new MutableBoolean();
      
      final MutableIntSet receiverVals;
      if (constParams != null && constParams[0] != null) {
        receiverVals = IntSetUtil.make();
        for(InstanceKey ik : constParams[0]) {
          receiverVals.add(system.getInstanceIndex(ik));
        }
      } else {
        receiverVals = rhs[0].getValue();
      }
      
      if (receiverVals == null) {
        // this constraint was put on the work list, probably by
        // initialization,
        // even though the right-hand-side is empty.
        // TODO: be more careful about what goes on the worklist to
        // avoid this.
        if (DEBUG) {
          System.err.println("EVAL dispatch with value null");
        }
        return NOT_CHANGED;        
        
      }
      // we handle the parameter positions one by one, rather than enumerating
      // the cartesian product of possibilities. this disallows
      // context-sensitivity policies like true CPA, but is necessary for
      // performance.
      InstanceKey keys[] = new InstanceKey[constParams == null? dispatchIndices[dispatchIndices.length-1]+1: constParams.length];
      // determine whether we're handling a new receiver; used later
      // to check for redundancy
      boolean newReceiver = !receiverVals.isSubset(previousPtrs[0]);
      // keep separate rhsIndex, since it doesn't advance for constant
      // parameters
      int rhsIndex = (constParams != null && constParams[0] != null)? 0: 1;
      // this flag is set to true if we ever call handleAllReceivers() in the
      // loop below. we need to catch the case where we have a new receiver, but
      // there are no other dispatch indices with new values
      boolean propagatedReceivers = false;
      // we start at index 1 since we need to handle the receiver specially; see
      // below
      for (int index = 1; index < dispatchIndices.length; index++) {
        try {
          MonitorUtil.throwExceptionIfCanceled(monitor);
        } catch (CancelException e) {
          throw new CancelRuntimeException(e);
        }
        int paramIndex = dispatchIndices[index];
        assert keys[paramIndex] == null;
        final MutableIntSet prevAtIndex = previousPtrs[index];
        if (constParams != null && constParams[paramIndex] != null) {
          // we have a constant parameter.  only need to propagate again if we've never done it before or if we have a new receiver
          if (newReceiver || prevAtIndex.isEmpty()) {
            for(int i = 0; i < constParams[paramIndex].length; i++) {
              keys[paramIndex] = constParams[paramIndex][i];
              handleAllReceivers(receiverVals,keys, addedNewTarget);
              propagatedReceivers = true;
              int ii = system.instanceKeys.getMappedIndex(constParams[paramIndex][i]);
              prevAtIndex.add(ii);
            }            
          }
        } else { // non-constant parameter
          PointsToSetVariable v = rhs[rhsIndex];
          if (v.getValue() != null) {
            IntIterator ptrs = v.getValue().intIterator();
            while (ptrs.hasNext()) {
              int ptr = ptrs.next();
              if (newReceiver || !prevAtIndex.contains(ptr)) {
                keys[paramIndex] = system.getInstanceKey(ptr);
                handleAllReceivers(receiverVals,keys, addedNewTarget);
                propagatedReceivers = true;
                prevAtIndex.add(ptr);
              }
            }
          } 
          rhsIndex++;
        }
        keys[paramIndex] = null;
      }
      if (newReceiver) {
        if (!propagatedReceivers) {
          // we have a new receiver value, and it wasn't propagated at all,
          // so propagate it now
          handleAllReceivers(receiverVals, keys, addedNewTarget);
        }
        // update receiver cache
        previousPtrs[0].addAll(receiverVals);
      }

      byte sideEffectMask = addedNewTarget.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    private void handleAllReceivers(MutableIntSet receiverVals, InstanceKey[] keys, MutableBoolean sideEffect) {
      assert keys[0] == null;
      IntIterator receiverIter = receiverVals.intIterator();
      while (receiverIter.hasNext()) {
        final int rcvr = receiverIter.next();
        keys[0] = system.getInstanceKey(rcvr);
        if (clone2Assign) {
          // for efficiency: assume that only call sites that reference
          // clone() might dispatch to clone methods
          if (call.getCallSite().getDeclaredTarget().getSelector().equals(cloneSelector)) {
            IClass recv = (keys[0] != null) ? keys[0].getConcreteType() : null;
            IMethod targetMethod = getOptions().getMethodTargetSelector().getCalleeTarget(node, call.getCallSite(), recv);
            if (targetMethod != null && targetMethod.getReference().equals(CloneInterpreter.CLONE)) {
              // treat this call to clone as an assignment
              PointerKey result = getPointerKeyForLocal(node, call.getDef());
              PointerKey receiver = getPointerKeyForLocal(node, call.getReceiver());
              system.newConstraint(result, assignOperator, receiver);
              return;
            }
          }
        }
        CGNode target = getTargetForCall(node, call.getCallSite(), keys[0].getConcreteType(), keys);
        if (target == null) {
          // This indicates an error; I sure hope getTargetForCall
          // raised a warning about this!
          if (DEBUG) {
            System.err.println("Warning: null target for call " + call);
          }
        } else {
          IntSet targets = getCallGraph().getPossibleTargetNumbers(node, call.getCallSite());
          // even if we've seen this target before, if we have constant
          // parameters, we may need to re-process the call, as the constraints
          // for the first time we reached this target may not have been fully
          // general. TODO a more refined check?
          if (targets != null && targets.contains(target.getGraphNodeId()) && noConstParams()) {
            // do nothing; we've previously discovered and handled this
            // receiver for this call site.
          } else {
            // process the newly discovered target for this call
            sideEffect.b = true;
            processResolvedCall(node, call, target, constParams, uniqueCatch);
            if (!haveAlreadyVisited(target)) {
              markDiscovered(target);
            }
          }
        }
      }
      keys[0] = null;
    }

    private boolean noConstParams() {
      if (constParams != null) {
        for (int i = 0; i < constParams.length; i++) {
          if (constParams[i] != null) {
            for (int j = 0; j < constParams[i].length; i++) {
              if (constParams[i][j] != null) {
                return false;
              }
            }
          }
        }
      }
      return true;
    }



    @Override
    public String toString() {
      return "Dispatch to " + call + " in node " + node;
    }

    @Override
    public int hashCode() {
      return node.hashCode() + 90289 * call.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      // note that these are not necessarily canonical, since
      // with synthetic factories we may regenerate constraints
      // many times. TODO: change processing of synthetic factories
      // so that we guarantee to insert each dispatch equation
      // only once ... if this were true we could optimize this
      // with reference equality

      // instanceof is OK because this class is final
      if (o instanceof DispatchOperator) {
        return node.equals(other.node) && call.equals(other.call) && Arrays.deepEquals(constParams, other.constParams);
      } else {
        return false;
      }
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  protected void iterateCrossProduct(final CGNode caller, final SSAAbstractInvokeInstruction call, IntSet parameters,
      final InstanceKey[][] invariants, final VoidFunction f) {
    final int params[] = IntSetUtil.toArray(parameters);
    final InstanceKey[] keys = new InstanceKey[call.getNumberOfParameters()];
    final CallSiteReference site = call.getCallSite();
    new Object() {
      private void rec(final int pi) {
        if (pi == params.length) {
          f.apply(keys);
        } else {
          final int p = params[pi];
          int vn = call.getUse(p);
          PointerKey var = getPointerKeyForLocal(caller, vn);
          InstanceKey[] ik = invariants != null ? invariants[p] : null;
          if (ik != null) {
            if (ik.length > 0) {
              for (int i = 0; i < ik.length; i++) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                keys[p] = ik[i];
                rec(pi + 1);
              }
            } else {
              if (!site.isDispatch() || p != 0) {
                keys[p] = null;
                rec(pi + 1);
              }
          } else {
            IntSet s = system.findOrCreatePointsToSet(var).getValue();
            if (s != null && !s.isEmpty()) {
              s.foreach(new IntSetAction() {
                public void act(int x) {
                  keys[p] = system.getInstanceKey(x);
                  rec(pi + 1);
                }
              });
            } else {
              if (!site.isDispatch() || p != 0) {
                keys[p] = null;
                rec(pi + 1);
              }
            }
          }
        }
      }
    }.rec(0);
  }
  
  protected Set getTargetsForCall(final CGNode caller, final SSAAbstractInvokeInstruction instruction, InstanceKey[][] invs) {
    // This method used to take a CallSiteReference as a parameter, rather than
    // an SSAAbstractInvokeInstruction. This was bad, since it's
    // possible for multiple invoke instructions with different actual
    // parameters to be associated with a single CallSiteReference. Changed
    // to take the invoke instruction as a parameter instead, since invs is
    // associated with the instruction
    final CallSiteReference site = instruction.getCallSite();
    IntSet params = contextSelector.getRelevantParameters(caller, site);
    if (!site.isStatic() && !params.contains(0)) {
      params = IntSetUtil.makeMutableCopy(params);
      ((MutableIntSet)params).add(0);
    }
    final Set targets = HashSetFactory.make();
    VoidFunction f = new VoidFunction() {
      public void apply(InstanceKey[] v) {
        IClass recv = null;
        if (site.isDispatch()) {
          recv = v[0].getConcreteType();
        }
        CGNode target = getTargetForCall(caller, site, recv, v);
        if (target != null) {
          targets.add(target);
        }
      }
    };
    iterateCrossProduct(caller, instruction, params, invs, f);
     return targets;
  }

  public boolean hasNoInterestingUses(CGNode node, int vn, DefUse du) {

    if (du == null) {
      throw new IllegalArgumentException("du is null");
    }
    if (vn <= 0) {
      throw new IllegalArgumentException("v is invalid: " + vn);
    }
    // todo: enhance this by solving a dead-code elimination
    // problem.
    InterestingVisitor v = makeInterestingVisitor(node, vn);
    for (Iterator it = du.getUses(v.vn); it.hasNext();) {
      SSAInstruction s = (SSAInstruction) it.next();
      s.visit(v);
      if (v.bingo) {
        return false;
      }
    }
    return true;
  }

  protected InterestingVisitor makeInterestingVisitor(CGNode node, int vn) {
    return new InterestingVisitor(vn);
  }

  /**
   * sets bingo to true when it visits an interesting instruction
   */
  protected static class InterestingVisitor extends SSAInstruction.Visitor {
    protected final int vn;

    protected InterestingVisitor(int vn) {
      this.vn = vn;
    }

    protected boolean bingo = false;

    @Override
    public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
      if (!instruction.typeIsPrimitive() && instruction.getArrayRef() == vn) {
        bingo = true;
      }
    }

    @Override
    public void visitArrayStore(SSAArrayStoreInstruction instruction) {
      if (!instruction.typeIsPrimitive() && (instruction.getArrayRef() == vn || instruction.getValue() == vn)) {
        bingo = true;
      }
    }

    @Override
    public void visitCheckCast(SSACheckCastInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitGet(SSAGetInstruction instruction) {
      FieldReference field = instruction.getDeclaredField();
      if (!field.getFieldType().isPrimitiveType()) {
        bingo = true;
      }
    }

    @Override
    public void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitInvoke(SSAInvokeInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitPhi(SSAPhiInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitPi(SSAPiInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitPut(SSAPutInstruction instruction) {
      FieldReference field = instruction.getDeclaredField();
      if (!field.getFieldType().isPrimitiveType()) {
        bingo = true;
      }
    }

    @Override
    public void visitReturn(SSAReturnInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitThrow(SSAThrowInstruction instruction) {
      bingo = true;
    }
  }

  /**
   * TODO: enhance this logic using type inference
   * 
   * @param instruction
   * @return true if we need to filter the receiver type to account for virtual dispatch
   */
  @SuppressWarnings("unused")
  private boolean needsFilterForReceiver(SSAAbstractInvokeInstruction instruction, CGNode target) {

    FilteredPointerKey.TypeFilter f = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.PARAMETERS[0]);

    if (f != null) {
        return true;
      // the context selects a particular concrete type for the receiver.
      // we need to filter, unless the declared receiver type implies the
      // concrete type (TODO: need to implement this optimization)
      return true;
    }

    // don't need to filter for invokestatic
    if (instruction.getCallSite().isStatic() || instruction.getCallSite().isSpecial()) {
      return false;
    }

    MethodReference declaredTarget = instruction.getDeclaredTarget();
    IMethod resolvedTarget = getClassHierarchy().resolveMethod(declaredTarget);
    if (resolvedTarget == null) {
      // there's some problem that will be flagged as a warning
      return true;
    }

    return true;
  }

  private boolean isRootType(IClass klass) {
    return klass.getClassHierarchy().isRootClass(klass);
  }

  @SuppressWarnings("unused")
  private boolean isRootType(FilteredPointerKey.TypeFilter filter) {
    if (filter instanceof FilteredPointerKey.SingleClassFilter) {
      return isRootType(((FilteredPointerKey.SingleClassFilter) filter).getConcreteType());
    } else {
      return false;
    }
  }

  /**
   * TODO: enhance this logic using type inference TODO!!!: enhance filtering to consider concrete types, not just cones.
   * precondition: needs Filter
   * 
   * @param target
   * @return an IClass which represents
   */
  protected PointerKey getTargetPointerKey(CGNode target, int index) {
    int vn;
    if (target.getIR() != null) {
      vn = target.getIR().getSymbolTable().getParameter(index);
    } else {
      vn = index+1;
    }

    FilteredPointerKey.TypeFilter filter = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.PARAMETERS[index]);
    if (filter != null && !filter.isRootFilter()) {
        return pointerKeyFactory.getFilteredPointerKeyForLocal(target, vn, filter);
    
    } else if (index == 0 && !target.getMethod().isStatic()) {
      // the context does not select a particular concrete type for the
      // receiver, so use the type of the method
      IClass C = getReceiverClass(target.getMethod());
      if (C.getClassHierarchy().getRootClass().equals(C)) {
        return pointerKeyFactory.getPointerKeyForLocal(target, vn);        
      } else {
        return pointerKeyFactory.getFilteredPointerKeyForLocal(target, vn, new FilteredPointerKey.SingleClassFilter(C));
      }
      
    } else {
      return pointerKeyFactory.getPointerKeyForLocal(target, vn);
    }
  }

  /**
   * @param method
   * @return the receiver class for this method.
   */
  private IClass getReceiverClass(IMethod method) {
    TypeReference formalType = method.getParameterType(0);
    IClass C = getClassHierarchy().lookupClass(formalType);
    if (method.isStatic()) {
      Assertions.UNREACHABLE("asked for receiver of static method " + method);
    }
    if (C == null) {
      Assertions.UNREACHABLE("no class found for " + formalType + " recv of " + method);
    }
    return C;
  }

  /**
   * A value is "invariant" if we can figure out the instances it can ever point to locally, without resorting to propagation.
   * 
   * @param valueNumber
   * @return true iff the contents of the local with this value number can be deduced locally, without propagation
   */
  protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumber) {
    if (isConstantRef(symbolTable, valueNumber)) {
      return true;
    } else if (SHORT_CIRCUIT_INVARIANT_SETS) {
      SSAInstruction def = du.getDef(valueNumber);
      if (def instanceof SSANewInstruction) {
      } else {
        return false;
      }
    } else {
      return false;
    }
  }

  protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumbers[]) {
    for(int i = 0; i < valueNumbers.length; i++) {
      if (! contentsAreInvariant(symbolTable, du, valueNumbers[i])) {
        return false;
      }
    }
    return true;
  }
  
  /**
   * precondition:contentsAreInvariant(valueNumber)
   * 
   * @param valueNumber
   * @return the complete set of instances that the local with vn=valueNumber may point to.
   */
  protected InstanceKey[] getInvariantContents(SymbolTable symbolTable, DefUse du, CGNode node, int valueNumber, HeapModel hm) {
    return getInvariantContents(symbolTable, du, node, valueNumber, hm, false);
  }

  protected InstanceKey[] getInvariantContents(SymbolTable symbolTable, DefUse du, CGNode node, int valueNumber, HeapModel hm,
      boolean ensureIndexes) {
    InstanceKey[] result;
    if (isConstantRef(symbolTable, valueNumber)) {
      Object x = symbolTable.getConstantValue(valueNumber);
      if (x instanceof String) {
        // this is always the case in Java. use strong typing in the call to getInstanceKeyForConstant.
        String S = (String) x;
        TypeReference type = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getConstantType(S);
        if (type == null) {
          return new InstanceKey[0];
        }
        InstanceKey ik = hm.getInstanceKeyForConstant(type, S);
        if (ik != null) {
          result = new InstanceKey[] { ik };
        } else {
          result = new InstanceKey[0];
        }
      } else {
        // some non-built in type (e.g. Integer). give up on strong typing.
        // language-specific subclasses (e.g. Javascript) should override this method to get strong typing
        // with generics if desired.
        TypeReference type = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getConstantType(x);
        if (type == null) {
          return new InstanceKey[0];
        }
        InstanceKey ik = hm.getInstanceKeyForConstant(type, x);
        if (ik != null) {
          result = new InstanceKey[] { ik };
        } else {
          result = new InstanceKey[0];
        }
      }
    } else {
      SSANewInstruction def = (SSANewInstruction) du.getDef(valueNumber);
      InstanceKey iKey = hm.getInstanceKeyForAllocation(node, def.getNewSite());
      result = (iKey == null) ? new InstanceKey[0] : new InstanceKey[] { iKey };
    }

    if (ensureIndexes) {
      for (int i = 0; i < result.length; i++) {
        system.findOrCreateIndexForInstanceKey(result[i]);
      }
    }

    return result;
  }

  protected boolean isConstantRef(SymbolTable symbolTable, int valueNumber) {
    if (valueNumber == -1) {
      return false;
    }
    if (symbolTable.isConstant(valueNumber)) {
      Object v = symbolTable.getConstantValue(valueNumber);
      return (!(v instanceof Number));
    } else {
      return false;
    }
  }

  /**
   * @author sfink
   * 
   *         A warning for when we fail to resolve the type for a checkcast
   */
  private static class CheckcastFailure extends Warning {

    final TypeReference type;

    CheckcastFailure(TypeReference type) {
      super(Warning.SEVERE);
      this.type = type;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + type;
    }

    public static CheckcastFailure create(TypeReference type) {
      return new CheckcastFailure(type);
    }
  }

  /**
   * @author sfink
   * 
   *         A warning for when we fail to resolve the type for a field
   */
  private static class FieldResolutionFailure extends Warning {

    final FieldReference field;

    FieldResolutionFailure(FieldReference field) {
      super(Warning.SEVERE);
      this.field = field;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + field;
    }

    public static FieldResolutionFailure create(FieldReference field) {
      return new FieldResolutionFailure(field);
    }
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.HeapModel#iteratePointerKeys()
   */
  public Iterator iteratePointerKeys() {
    return system.iteratePointerKeys();
  }

  public static Set getCaughtExceptionTypes(SSAGetCaughtExceptionInstruction instruction, IR ir) {
    if (ir == null) {
      throw new IllegalArgumentException("ir is null");
    }
    if (instruction == null) {
      throw new IllegalArgumentException("instruction is null");
    }
    Iterator exceptionTypes = ((ExceptionHandlerBasicBlock) ir.getControlFlowGraph().getNode(
        instruction.getBasicBlockNumber())).getCaughtExceptionTypes();
    HashSet types = HashSetFactory.make(10);
    for (; exceptionTypes.hasNext();) {
      IClass c = ir.getMethod().getClassHierarchy().lookupClass(exceptionTypes.next());
      if (c != null) {
        types.add(c);
      }
    }
    return types;
  }

  public InstanceKey getInstanceKeyForPEI(CGNode node, ProgramCounter instr, TypeReference type) {
    return getInstanceKeyForPEI(node, instr, type, instanceKeyFactory);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder#makeSolver()
   */
  @Override
  protected IPointsToSolver makeSolver() {
    return new StandardSolver(system, this);
    // return usePreTransitiveSolver ? (IPointsToSolver) new PreTransitiveSolver(system, this) : new StandardSolver(system, this);
    // return true ? (IPointsToSolver)new PreTransitiveSolver(system,this) : new
    // StandardSolver(system,this);
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
    return true;
  }

  /**

    /**

  /**
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.callgraph.propagation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.ibm.wala.analysis.reflection.CloneInterpreter;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.classLoader.ProgramCounter;
import com.ibm.wala.fixpoint.AbstractOperator;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.ContextKey;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.impl.AbstractRootMethod;
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
import com.ibm.wala.ipa.callgraph.impl.FakeRootMethod;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.ConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAAbstractThrowInstruction;
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSACFG;
import com.ibm.wala.ssa.SSACFG.BasicBlock;
import com.ibm.wala.ssa.SSACFG.ExceptionHandlerBasicBlock;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAInstanceofInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.CancelRuntimeException;
import com.ibm.wala.util.MonitorUtil;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.functions.VoidFunction;
import com.ibm.wala.util.intset.IntIterator;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetAction;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.ref.ReferenceCleanser;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * This abstract base class provides the general algorithm for a call graph builder that relies on propagation through an iterative
 * dataflow solver, and constraints generated by statements in SSA form.
 * 
 * TODO: This implementation currently keeps all points to sets live ... even those for local variables that do not span
 * interprocedural boundaries. This may be too space-inefficient .. we can consider recomputing local sets on demand.
 */
public abstract class SSAPropagationCallGraphBuilder extends PropagationCallGraphBuilder implements HeapModel {
  private final static boolean DEBUG = false;

  private final static boolean DEBUG_MULTINEWARRAY = DEBUG | false;

  /**
   * Should we periodically clear out soft reference caches in an attempt to help the GC?
   */
  public final static boolean PERIODIC_WIPE_SOFT_CACHES = true;

  /**
   * Interval which defines the period to clear soft reference caches
   */
  public final static int WIPE_SOFT_CACHE_INTERVAL = 2500;

  /**
   * Counter for wiping soft caches
   */
  private static int wipeCount = 0;

  /**
   * use type inference to avoid unnecessary filter constraints?
   */
  // private final static boolean OPTIMIZE_WITH_TYPE_INFERENCE = true;
  /**
   * An optimization: if we can locally determine the final solution for a points-to set, then don't actually create the points-to
   * set, but instead short circuit by propagating the final solution to all such uses.
   * 
   * String constants are ALWAYS considered invariant, regardless of the value of this flag.
   * 
   * However, if this flag is set, then the solver is more aggressive identifying invariants.
   * 
   * Doesn't play well with pre-transitive solver; turning off for now.
   */
  private final static boolean SHORT_CIRCUIT_INVARIANT_SETS = true;
   * An optimization: if we can locally determine that a particular pointer p has exactly one use, then we don't actually create the
   * points-to-set for p, but instead short-circuit by propagating the final solution to the unique use.
   * 
   * Doesn't play well with pre-transitive solver; turning off for now.
   */
  protected final static boolean SHORT_CIRCUIT_SINGLE_USES = true;

  /**
   * Should we change calls to clone() to assignments?
   */
  private final boolean clone2Assign = false;

  /**
   * Cache for efficiency
   */
  private final static Selector cloneSelector = CloneInterpreter.CLONE.getSelector();

  /**
   * set of class whose clinits have already been processed
   */
  private final Set clinitVisited = HashSetFactory.make();

  private IProgressMonitor monitor;

  protected SSAPropagationCallGraphBuilder(IClassHierarchy cha, AnalysisOptions options, AnalysisCache cache,
      PointerKeyFactory pointerKeyFactory) {
    super(cha, options, cache, pointerKeyFactory);
    // this.usePreTransitiveSolver = options.usePreTransitiveSolver();
  }

  public SSAContextInterpreter getCFAContextInterpreter() {
    return (SSAContextInterpreter) getContextInterpreter();
  }

  /**
   * @param node
   * @param x
   * @param type
   * @return the instance key that represents the exception of type _type_ thrown by a particular PEI.
   * @throws IllegalArgumentException if ikFactory is null
   */
  public static InstanceKey getInstanceKeyForPEI(CGNode node, ProgramCounter x, TypeReference type, InstanceKeyFactory ikFactory) {
    if (ikFactory == null) {
      throw new IllegalArgumentException("ikFactory is null");
    }
    return ikFactory.getInstanceKeyForPEI(node, x, type);
  }

  /**
   * Visit all instructions in a node, and add dataflow constraints induced by each statement in the SSA form.
   * @throws CancelException 
   * 
   * @see com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder#addConstraintsFromNode(com.ibm.wala.ipa.callgraph.CGNode)
   */
  @Override
  protected boolean addConstraintsFromNode(CGNode node, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    if (haveAlreadyVisited(node)) {
      return false;
    } else {
      markAlreadyVisited(node);
    }
    return unconditionallyAddConstraintsFromNode(node, monitor);
  }

  @Override
  protected boolean unconditionallyAddConstraintsFromNode(CGNode node, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    if (PERIODIC_WIPE_SOFT_CACHES) {
      wipeCount++;
      if (wipeCount >= WIPE_SOFT_CACHE_INTERVAL) {
        wipeCount = 0;
        ReferenceCleanser.clearSoftCaches();
      }
    }

    if (DEBUG) {
      System.err.println("\n\nAdd constraints from node " + node);
    }
    IR ir = getCFAContextInterpreter().getIR(node);
    if (DEBUG) {
      if (ir == null) {
        System.err.println("\n   No statements\n");
      } else {
        try {
          System.err.println(ir.toString());
        } catch (Error e) {
          e.printStackTrace();
        }
      }
    }

    if (ir == null) {
      return false;
    }

    addNodeInstructionConstraints(node, monitor);

    DefUse du = getCFAContextInterpreter().getDU(node);
    addNodePassthruExceptionConstraints(node, ir, du);
    // conservatively assume something changed
  }

  /**
   * @return a visitor to examine instructions in the ir
   */
  protected ConstraintVisitor makeVisitor(CGNode node) {
    return new ConstraintVisitor(this, node);
  }

  /**
   * Add pointer flow constraints based on instructions in a given node
   * @throws CancelException 
   */
  protected void addNodeInstructionConstraints(CGNode node, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    ConstraintVisitor v = makeVisitor(node);

    IR ir = v.ir;
    ControlFlowGraph cfg = ir.getControlFlowGraph();
    for (Iterator x = cfg.iterator(); x.hasNext();) {
      BasicBlock b = (BasicBlock) x.next();
      addBlockInstructionConstraints(node, cfg, b, v, monitor);
      if (wasChanged(node)) {
        return;
      }
    }
  }

  /**
   * Add constraints for a particular basic block.
   * @throws CancelException 
   */
  protected void addBlockInstructionConstraints(CGNode node, ControlFlowGraph cfg, BasicBlock b,
      ConstraintVisitor v, IProgressMonitor monitor) throws CancelException {
    this.monitor = monitor;
    v.setBasicBlock(b);

    // visit each instruction in the basic block.
    for (Iterator it = b.iterator(); it.hasNext();) {
      MonitorUtil.throwExceptionIfCanceled(monitor);      
      SSAInstruction s = it.next();
      if (s != null) {
        s.visit(v);
        if (wasChanged(node)) {
          return;
        }
      }
    }

    addPhiConstraints(node, cfg, b, v);
  }

  private void addPhiConstraints(CGNode node, ControlFlowGraph cfg, BasicBlock b,
      ConstraintVisitor v) {
    // visit each phi instruction in each successor block
    for (Iterator sbs = cfg.getSuccNodes(b); sbs.hasNext();) {
      BasicBlock sb = (BasicBlock) sbs.next();
      if (!sb.hasPhi()) {
        continue;
      }
      int n = 0;
      for (Iterator back = cfg.getPredNodes(sb); back.hasNext(); n++) {
        if (back.next() == b) {
          break;
        }
      }
      assert n < cfg.getPredNodeCount(sb);
      for (Iterator phis = sb.iteratePhis(); phis.hasNext();) {
        SSAPhiInstruction phi = (SSAPhiInstruction) phis.next();
        if (phi == null) {
          continue;
        }
        PointerKey def = getPointerKeyForLocal(node, phi.getDef());
        if (hasNoInterestingUses(node, phi.getDef(), v.du)) {
          system.recordImplicitPointsToSet(def);
        } else {
          // the following test restricts the constraints to reachable
          // paths, according to verification constraints
          if (phi.getUse(n) > 0) {
            PointerKey use = getPointerKeyForLocal(node, phi.getUse(n));
            if (contentsAreInvariant(v.symbolTable, v.du, phi.getUse(n))) {
              system.recordImplicitPointsToSet(use);
              InstanceKey[] ik = getInvariantContents(v.symbolTable, v.du, node, phi.getUse(n), this);
              for (int i = 0; i < ik.length; i++) {
                system.newConstraint(def, ik[i]);
              }
            } else {
              system.newConstraint(def, assignOperator, use);
            }
          }
        }
      }
    }
  }

  /**
   * Add constraints to represent the flow of exceptions to the exceptional return value for this node
   * 
   * @param node
   * @param ir
   */
  protected void addNodePassthruExceptionConstraints(CGNode node, IR ir, DefUse du) {
    // add constraints relating to thrown exceptions that reach the exit block.
    List peis = getIncomingPEIs(ir, ir.getExitBlock());
    PointerKey exception = getPointerKeyForExceptionalReturnValue(node);

    TypeReference throwableType = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getThrowableType();
    IClass c = node.getClassHierarchy().lookupClass(throwableType);
    addExceptionDefConstraints(ir, du, node, peis, exception, Collections.singleton(c));
  }

  /**
   * Generate constraints which assign exception values into an exception pointer
   * 
   * @param node governing node
   * @param peis list of PEI instructions
   * @param exceptionVar PointerKey representing a pointer to an exception value
   * @param catchClasses the types "caught" by the exceptionVar
   */
  private void addExceptionDefConstraints(IR ir, DefUse du, CGNode node, List peis, PointerKey exceptionVar,
      Set catchClasses) {
    if (DEBUG) {
      System.err.println("Add exception def constraints for node " + node);
    }
    for (Iterator it = peis.iterator(); it.hasNext();) {
      ProgramCounter peiLoc = it.next();
      if (DEBUG) {
        System.err.println("peiLoc: " + peiLoc);
      }
      SSAInstruction pei = ir.getPEI(peiLoc);

      if (DEBUG) {
        System.err.println("Add exceptions from pei " + pei);
      }

      if (pei instanceof SSAAbstractInvokeInstruction) {
        SSAAbstractInvokeInstruction s = (SSAAbstractInvokeInstruction) pei;
        PointerKey e = getPointerKeyForLocal(node, s.getException());

        if (!SHORT_CIRCUIT_SINGLE_USES || !hasUniqueCatchBlock(s, ir)) {
          addAssignmentsForCatchPointerKey(exceptionVar, catchClasses, e);
        }// else {
        // System.err.println("SKIPPING ASSIGNMENTS TO " + exceptionVar + " FROM " +
        // e);
        // }
      } else if (pei instanceof SSAAbstractThrowInstruction) {
        SSAAbstractThrowInstruction s = (SSAAbstractThrowInstruction) pei;
        PointerKey e = getPointerKeyForLocal(node, s.getException());

        if (contentsAreInvariant(ir.getSymbolTable(), du, s.getException())) {
          InstanceKey[] ik = getInvariantContents(ir.getSymbolTable(), du, node, s.getException(), this);
          for (int i = 0; i < ik.length; i++) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            assignInstanceToCatch(exceptionVar, catchClasses, ik[i]);
          }
        } else {
          addAssignmentsForCatchPointerKey(exceptionVar, catchClasses, e);
        }
      }

      // Account for those exceptions for which we do not actually have a
      // points-to set for
      // the pei, but just instance keys
      Collection types = pei.getExceptionTypes();
      if (types != null) {
        for (Iterator it2 = types.iterator(); it2.hasNext();) {
          TypeReference type = it2.next();
          if (type != null) {
            InstanceKey ik = getInstanceKeyForPEI(node, peiLoc, type, instanceKeyFactory);
            if (ik == null) {
              continue;
            }
            assert ik instanceof ConcreteTypeKey : "uh oh: need to implement getCaughtException constraints for instance " + ik;
            ConcreteTypeKey ck = (ConcreteTypeKey) ik;
            IClass klass = ck.getType();
            if (PropagationCallGraphBuilder.catches(catchClasses, klass, cha)) {
              system.newConstraint(exceptionVar, getInstanceKeyForPEI(node, peiLoc, type, instanceKeyFactory));
            }
          }
        }
      }
    }
  }

  /**
   * @return true iff there's a unique catch block which catches all exceptions thrown by a certain call site.
   */
    protected final IR ir;
  protected static boolean hasUniqueCatchBlock(SSAAbstractInvokeInstruction call, IR ir) {
    ISSABasicBlock[] bb = ir.getBasicBlocksForCall(call.getCallSite());
    if (bb.length == 1) {
      Iterator it = ir.getControlFlowGraph().getExceptionalSuccessors(bb[0]).iterator();
      // check that there's exactly one element in the iterator
      if (it.hasNext()) {
        it.next();
        return (!it.hasNext());
      }
    }
    return false;
   * precondition: hasUniqueCatchBlock(call,node,cg)
   * 
   * @return the unique pointer key which catches the exceptions thrown by a call
   * @throws IllegalArgumentException if ir == null
   * @throws IllegalArgumentException if call == null
   */
  public PointerKey getUniqueCatchKey(SSAAbstractInvokeInstruction call, IR ir, CGNode node) throws IllegalArgumentException,
      IllegalArgumentException {
    if (call == null) {
      throw new IllegalArgumentException("call == null");
    }
    if (ir == null) {
      throw new IllegalArgumentException("ir == null");
    }
    ISSABasicBlock[] bb = ir.getBasicBlocksForCall(call.getCallSite());
    assert bb.length == 1;
    SSACFG.BasicBlock cb = (BasicBlock) ir.getControlFlowGraph().getExceptionalSuccessors(bb[0]).iterator().next();
    if (cb.isExitBlock()) {
      return getPointerKeyForExceptionalReturnValue(node);
    } else {
      SSACFG.ExceptionHandlerBasicBlock ehbb = (ExceptionHandlerBasicBlock) cb;
      SSAGetCaughtExceptionInstruction ci = ehbb.getCatchInstruction();
      return getPointerKeyForLocal(node, ci.getDef());
    }
  }

  /**
   * @return a List of Instructions that may transfer control to bb via an exceptional edge
   * @throws IllegalArgumentException if ir is null
   */
  public static List getIncomingPEIs(IR ir, ISSABasicBlock bb) {
    if (ir == null) {
      throw new IllegalArgumentException("ir is null");
    }
    if (DEBUG) {
      System.err.println("getIncomingPEIs " + bb);
    }
    ControlFlowGraph g = ir.getControlFlowGraph();
    List result = new ArrayList(g.getPredNodeCount(bb));
    for (Iterator it = g.getPredNodes(bb); it.hasNext();) {
      BasicBlock pred = (BasicBlock) it.next();
      if (DEBUG) {
        System.err.println("pred: " + pred);
      }
      if (pred.isEntryBlock())
        continue;
      int index = pred.getLastInstructionIndex();
      SSAInstruction pei = ir.getInstructions()[index];
      // Note: pei might be null if pred is unreachable.
      // TODO: consider pruning CFG for unreachable blocks.
      if (pei != null && pei.isPEI()) {
        if (DEBUG) {
          System.err.println("PEI: " + pei + " index " + index + " PC " + g.getProgramCounter(index));
        }
        result.add(new ProgramCounter(g.getProgramCounter(index)));
      }
    }
    return result;
  }

  /**
   * A visitor that generates constraints based on statements in SSA form.
   */
  protected static class ConstraintVisitor extends SSAInstruction.Visitor {

    /**
     * The governing call graph builder. This field is used instead of an inner class in order to allow more flexible reuse of this
     * visitor in subclasses
     */
    protected final SSAPropagationCallGraphBuilder builder;

    /**
     * The node whose statements we are currently traversing
     */
    protected final CGNode node;

    /**
     * The governing call graph.
     */
    private final ExplicitCallGraph callGraph;

    /**
     * The governing IR
     */

    /**
     * The governing propagation system, into which constraints are added
     */
    protected final PropagationSystem system;

    /**
     * The basic block currently being processed
     */
    protected ISSABasicBlock basicBlock;

    /**
     * Governing symbol table
     */
    protected final SymbolTable symbolTable;

     * Def-use information
     */
    protected final DefUse du;

    public ConstraintVisitor(SSAPropagationCallGraphBuilder builder, CGNode node) {
      this.builder = builder;
      this.node = node;

      this.callGraph = builder.getCallGraph();

      this.system = builder.getPropagationSystem();

      this.ir = builder.getCFAContextInterpreter().getIR(node);
      this.symbolTable = this.ir.getSymbolTable();

      this.du = builder.getCFAContextInterpreter().getDU(node);

      assert symbolTable != null;
    }

    protected SSAPropagationCallGraphBuilder getBuilder() {
      return builder;
    }

    protected AnalysisOptions getOptions() {
      return builder.options;
    }

    protected AnalysisCache getAnalysisCache() {
      return builder.getAnalysisCache();
    }

    public PointerKey getPointerKeyForLocal(int valueNumber) {
      return getBuilder().getPointerKeyForLocal(node, valueNumber);
    }

    public FilteredPointerKey getFilteredPointerKeyForLocal(int valueNumber, FilteredPointerKey.TypeFilter filter) {
      return getBuilder().getFilteredPointerKeyForLocal(node, valueNumber, filter);
    }

    public PointerKey getPointerKeyForReturnValue() {
      return getBuilder().getPointerKeyForReturnValue(node);
    }

    public PointerKey getPointerKeyForExceptionalReturnValue() {
      return getBuilder().getPointerKeyForExceptionalReturnValue(node);
    }

    public PointerKey getPointerKeyForStaticField(IField f) {
      return getBuilder().getPointerKeyForStaticField(f);
    }

    public PointerKey getPointerKeyForInstanceField(InstanceKey I, IField f) {
      return getBuilder().getPointerKeyForInstanceField(I, f);
    }

    public PointerKey getPointerKeyForArrayContents(InstanceKey I) {
      return getBuilder().getPointerKeyForArrayContents(I);
    }

    public InstanceKey getInstanceKeyForAllocation(NewSiteReference allocation) {
      return getBuilder().getInstanceKeyForAllocation(node, allocation);
    }

    public InstanceKey getInstanceKeyForMultiNewArray(NewSiteReference allocation, int dim) {
      return getBuilder().getInstanceKeyForMultiNewArray(node, allocation, dim);
    }

    public  InstanceKey getInstanceKeyForConstant(T S) {
      TypeReference type = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getConstantType(S);
      return getBuilder().getInstanceKeyForConstant(type, S);
    }

    public InstanceKey getInstanceKeyForPEI(ProgramCounter instr, TypeReference type) {
      return getBuilder().getInstanceKeyForPEI(node, instr, type);
    }

    public InstanceKey getInstanceKeyForClassObject(TypeReference type) {
      return getBuilder().getInstanceKeyForClassObject(type);
    }

    public CGNode getTargetForCall(CGNode caller, CallSiteReference site, IClass recv, InstanceKey iKey[]) {
      return getBuilder().getTargetForCall(caller, site, recv, iKey);
    }

    protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumber) {
      return getBuilder().contentsAreInvariant(symbolTable, du, valueNumber);
    }

    protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumber[]) {
      return getBuilder().contentsAreInvariant(symbolTable, du, valueNumber);
    }

    protected InstanceKey[] getInvariantContents(int valueNumber) {
      return getInvariantContents(ir.getSymbolTable(), du, node, valueNumber);
    }

      if (DEBUG) {
    protected InstanceKey[] getInvariantContents(SymbolTable symbolTable, DefUse du, CGNode node, int valueNumber) {
      return getBuilder().getInvariantContents(symbolTable, du, node, valueNumber, getBuilder());
    }

    protected IClassHierarchy getClassHierarchy() {
      return getBuilder().getClassHierarchy();
    }

    protected boolean hasNoInterestingUses(int vn) {
      return getBuilder().hasNoInterestingUses(node, vn, du);
    }

    protected boolean isRootType(IClass klass) {
      return getBuilder().isRootType(klass);
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitArrayLoad(com.ibm.wala.ssa.SSAArrayLoadInstruction)
     */
    @Override
    public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
      // skip arrays of primitive type
      if (instruction.typeIsPrimitive()) {
        return;
      }
      doVisitArrayLoad(instruction.getDef(), instruction.getArrayRef());
    }

    protected void doVisitArrayLoad(int def, int arrayRef) {
      PointerKey result = getPointerKeyForLocal(def);
      PointerKey arrayRefPtrKey = getPointerKeyForLocal(arrayRef);
      if (hasNoInterestingUses(def)) {
        system.recordImplicitPointsToSet(result);
      } else {
        if (contentsAreInvariant(symbolTable, du, arrayRef)) {
          system.recordImplicitPointsToSet(arrayRefPtrKey);
          InstanceKey[] ik = getInvariantContents(arrayRef);
          for (int i = 0; i < ik.length; i++) {
            if (!representsNullType(ik[i])) {
              system.findOrCreateIndexForInstanceKey(ik[i]);
              PointerKey p = getPointerKeyForArrayContents(ik[i]);
              if (p == null) {
              } else {
                system.newConstraint(result, assignOperator, p);
              }
            }
          }
        } else {
          assert !system.isUnified(result);
          assert !system.isUnified(arrayRefPtrKey);
          system.newSideEffect(getBuilder().new ArrayLoadOperator(system.findOrCreatePointsToSet(result)), arrayRefPtrKey);
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitArrayStore(com.ibm.wala.ssa.SSAArrayStoreInstruction)
     */
    public void doVisitArrayStore(int arrayRef, int value) {
      // (requires the creation of assign constraints as
      // the set points-to(a[]) grows.)
      PointerKey valuePtrKey = getPointerKeyForLocal(value);
      PointerKey arrayRefPtrKey = getPointerKeyForLocal(arrayRef);
      // if (!supportFullPointerFlowGraph &&
      // contentsAreInvariant(instruction.getArrayRef())) {
      if (contentsAreInvariant(symbolTable, du, arrayRef)) {
        system.recordImplicitPointsToSet(arrayRefPtrKey);
        InstanceKey[] ik = getInvariantContents(arrayRef);

        for (int i = 0; i < ik.length; i++) {
          if (!representsNullType(ik[i]) && !(ik[i] instanceof ZeroLengthArrayInNode)) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            PointerKey p = getPointerKeyForArrayContents(ik[i]);
            IClass contents = ((ArrayClass) ik[i].getConcreteType()).getElementClass();
            if (p == null) {
            } else {
              if (contentsAreInvariant(symbolTable, du, value)) {
                system.recordImplicitPointsToSet(valuePtrKey);
        } else {
                InstanceKey[] vk = getInvariantContents(value);
                for (int j = 0; j < vk.length; j++) {
                  system.findOrCreateIndexForInstanceKey(vk[j]);
                  if (vk[j].getConcreteType() != null) {
                    if (getClassHierarchy().isAssignableFrom(contents, vk[j].getConcreteType())) {
                      system.newConstraint(p, vk[j]);
                    }
                  }
                }
              } else {
                if (isRootType(contents)) {

                  system.newConstraint(p, assignOperator, valuePtrKey);
                } else {
                  system.newConstraint(p, getBuilder().filterOperator, valuePtrKey);
                }
              }
            }
          }
        }
      } else {
        if (contentsAreInvariant(symbolTable, du, value)) {
          system.recordImplicitPointsToSet(valuePtrKey);
          InstanceKey[] ik = getInvariantContents(value);
          for (int i = 0; i < ik.length; i++) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            assert !system.isUnified(arrayRefPtrKey);
            system.newSideEffect(getBuilder().new InstanceArrayStoreOperator(ik[i]), arrayRefPtrKey);
          }
        } else {
          system.newSideEffect(getBuilder().new ArrayStoreOperator(system.findOrCreatePointsToSet(valuePtrKey)), arrayRefPtrKey);
        }
      }
    }

    @Override
    public void visitArrayStore(SSAArrayStoreInstruction instruction) {
      // skip arrays of primitive type
      if (instruction.typeIsPrimitive()) {
        return;
      }
      doVisitArrayStore(instruction.getArrayRef(), instruction.getValue());
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitCheckCast(com.ibm.wala.ssa.SSACheckCastInstruction)
     */
    @Override
    public void visitCheckCast(SSACheckCastInstruction instruction) {
 
      boolean isRoot = false;
    Set types = HashSetFactory.make();
      
      for(TypeReference t : instruction.getDeclaredResultTypes()) {
        IClass cls = getClassHierarchy().lookupClass(t);
        if (cls == null) {
          Warnings.add(CheckcastFailure.create(t));
         return;
        } else {
          if (isRootType(cls)) {
            isRoot = true;
          }
          types.add(cls);
        }
      }
      
      PointerKey result = getFilteredPointerKeyForLocal(instruction.getResult(), new FilteredPointerKey.MultipleClassesFilter(types.toArray(new IClass[ types.size() ])));
      PointerKey value = getPointerKeyForLocal(instruction.getVal());

      if (hasNoInterestingUses(instruction.getDef())) {
        system.recordImplicitPointsToSet(result);
      } else {
        if (contentsAreInvariant(symbolTable, du, instruction.getVal())) {
          system.recordImplicitPointsToSet(value);
          InstanceKey[] ik = getInvariantContents(instruction.getVal());
          for(TypeReference t : instruction.getDeclaredResultTypes()) {
            IClass cls = getClassHierarchy().lookupClass(t);

            if (cls.isInterface()) {
              for (int i = 0; i < ik.length; i++) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                if (getClassHierarchy().implementsInterface(ik[i].getConcreteType(), cls)) {
                  system.newConstraint(result, ik[i]);
                }
              }
            } else {
              for (int i = 0; i < ik.length; i++) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                if (getClassHierarchy().isSubclassOf(ik[i].getConcreteType(), cls)) {
                  system.newConstraint(result, ik[i]);
                }
              }
            }
          }
          if (isRoot) {
            system.newConstraint(result, assignOperator, value);
          } else {
            system.newConstraint(result, getBuilder().filterOperator, value);
          }
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitReturn(com.ibm.wala.ssa.SSAReturnInstruction)
     */
    @Override
    public void visitReturn(SSAReturnInstruction instruction) {
      // skip returns of primitive type
      if (instruction.returnsPrimitiveType() || instruction.returnsVoid()) {
        return;
      }
      if (DEBUG) {
        System.err.println("visitReturn: " + instruction);
      }

      PointerKey returnValue = getPointerKeyForReturnValue();
      PointerKey result = getPointerKeyForLocal(instruction.getResult());
      if (contentsAreInvariant(symbolTable, du, instruction.getResult())) {
        system.recordImplicitPointsToSet(result);
        InstanceKey[] ik = getInvariantContents(instruction.getResult());
        for (int i = 0; i < ik.length; i++) {
          if (DEBUG) {
            System.err.println("invariant contents: " + returnValue + " " + ik[i]);
          }
          system.newConstraint(returnValue, ik[i]);
        }
      } else {
        system.newConstraint(returnValue, assignOperator, result);
      }
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitGet(com.ibm.wala.ssa.SSAGetInstruction)
     */
    @Override
    public void visitGet(SSAGetInstruction instruction) {
      visitGetInternal(instruction.getDef(), instruction.getRef(), instruction.isStatic(), instruction.getDeclaredField());
    }

    protected void visitGetInternal(int lval, int ref, boolean isStatic, FieldReference field) {
      if (DEBUG) {
        System.err.println("visitGet " + field);
      }

      // skip getfields of primitive type (optimisation)
      if (field.getFieldType().isPrimitiveType()) {
        return;
      }
      PointerKey def = getPointerKeyForLocal(lval);
      assert def != null;

      IField f = getClassHierarchy().resolveField(field);
      if (f == null && callGraph.getFakeRootNode().getMethod().getDeclaringClass().getReference().equals(field.getDeclaringClass())) {
        f = callGraph.getFakeRootNode().getMethod().getDeclaringClass().getField(field.getName());
      }

      if (f == null) {
        return;
      }

      if (hasNoInterestingUses(lval)) {
        system.recordImplicitPointsToSet(def);
      } else {
        if (isStatic) {
          PointerKey fKey = getPointerKeyForStaticField(f);
          system.newConstraint(def, assignOperator, fKey);
          IClass klass = getClassHierarchy().lookupClass(field.getDeclaringClass());
          if (klass == null) {
          } else {
            // side effect of getstatic: may call class initializer
            if (DEBUG) {
              System.err.println("getstatic call class init " + klass);
            }
            processClassInitializer(klass);
          }
        } else {
          PointerKey refKey = getPointerKeyForLocal(ref);
          // if (!supportFullPointerFlowGraph &&
          // contentsAreInvariant(ref)) {
          if (contentsAreInvariant(symbolTable, du, ref)) {
            system.recordImplicitPointsToSet(refKey);
            InstanceKey[] ik = getInvariantContents(ref);
            for (int i = 0; i < ik.length; i++) {
              if (!representsNullType(ik[i])) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                PointerKey p = getPointerKeyForInstanceField(ik[i], f);
                system.newConstraint(def, assignOperator, p);
              }
            }
          } else {
            system.newSideEffect(getBuilder().new GetFieldOperator(f, system.findOrCreatePointsToSet(def)), refKey);
          }
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitPut(com.ibm.wala.ssa.PutInstruction)
     */
    @Override
    public void visitPut(SSAPutInstruction instruction) {
      if (iKey == null) {
      visitPutInternal(instruction.getVal(), instruction.getRef(), instruction.isStatic(), instruction.getDeclaredField());
    }

    public void visitPutInternal(int rval, int ref, boolean isStatic, FieldReference field) {

      if (DEBUG) {
        System.err.println("visitPut " + field);
      }

      // skip putfields of primitive type
      if (field.getFieldType().isPrimitiveType()) {
        return;
      }
      IField f = getClassHierarchy().resolveField(field);
      if (f == null) {
        if (DEBUG) {
          System.err.println("Could not resolve field " + field);
        }
        Warnings.add(FieldResolutionFailure.create(field));
        return;
      }
      assert f.getFieldTypeReference().getName().equals(field.getFieldType().getName()) :
        "name clash of two fields with the same name but different type: " + f.getReference() + " <=> " + field;
      assert isStatic || !symbolTable.isStringConstant(ref) : "put to string constant shouldn't be allowed?";
      if (isStatic) {
        processPutStatic(rval, field, f);
      } else {
        processPutField(rval, ref, f);
      }
    }

    private void processPutField(int rval, int ref, IField f) {
      assert !f.getFieldTypeReference().isPrimitiveType();
      PointerKey refKey = getPointerKeyForLocal(ref);
      PointerKey rvalKey = getPointerKeyForLocal(rval);
      // if (!supportFullPointerFlowGraph &&
      // contentsAreInvariant(rval)) {
      if (contentsAreInvariant(symbolTable, du, rval)) {
        system.recordImplicitPointsToSet(rvalKey);
        InstanceKey[] ik = getInvariantContents(rval);
        if (contentsAreInvariant(symbolTable, du, ref)) {
          system.recordImplicitPointsToSet(refKey);
          InstanceKey[] refk = getInvariantContents(ref);
          for (int j = 0; j < refk.length; j++) {
            if (!representsNullType(refk[j])) {
              system.findOrCreateIndexForInstanceKey(refk[j]);
              PointerKey p = getPointerKeyForInstanceField(refk[j], f);
              for (int i = 0; i < ik.length; i++) {
                system.newConstraint(p, ik[i]);
              }
            }
          }
        } else {
          for (int i = 0; i < ik.length; i++) {
            system.findOrCreateIndexForInstanceKey(ik[i]);
            system.newSideEffect(getBuilder().new InstancePutFieldOperator(f, ik[i]), refKey);
          }
        }
      } else {
        if (contentsAreInvariant(symbolTable, du, ref)) {
          system.recordImplicitPointsToSet(refKey);
          InstanceKey[] refk = getInvariantContents(ref);
          for (int j = 0; j < refk.length; j++) {
            if (!representsNullType(refk[j])) {
              system.findOrCreateIndexForInstanceKey(refk[j]);
              PointerKey p = getPointerKeyForInstanceField(refk[j], f);
              system.newConstraint(p, assignOperator, rvalKey);
            }
          }
        } else {
          if (DEBUG) {
            System.err.println("adding side effect " + f);
          }
          system.newSideEffect(getBuilder().new PutFieldOperator(f, system.findOrCreatePointsToSet(rvalKey)), refKey);
        }
      }
    }

    private void processPutStatic(int rval, FieldReference field, IField f) {
      PointerKey fKey = getPointerKeyForStaticField(f);
      PointerKey rvalKey = getPointerKeyForLocal(rval);

      // if (!supportFullPointerFlowGraph &&
      // contentsAreInvariant(rval)) {
      if (contentsAreInvariant(symbolTable, du, rval)) {
        system.recordImplicitPointsToSet(rvalKey);
        InstanceKey[] ik = getInvariantContents(rval);
        for (int i = 0; i < ik.length; i++) {
          system.newConstraint(fKey, ik[i]);
        }
      } else {
        system.newConstraint(fKey, assignOperator, rvalKey);
      }
        System.err.println("visitPut class init " + field.getDeclaringClass() + " " + field);
      }
      // side effect of putstatic: may call class initializer
      IClass klass = getClassHierarchy().lookupClass(field.getDeclaringClass());
      if (klass == null) {
        Warnings.add(FieldResolutionFailure.create(field));
      } else {
        processClassInitializer(klass);
      }
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitInvoke(com.ibm.wala.ssa.InvokeInstruction)
     */
    @Override
    public void visitInvoke(SSAInvokeInstruction instruction) {
      visitInvokeInternal(instruction, new DefaultInvariantComputer());
    }

    protected void visitInvokeInternal(final SSAAbstractInvokeInstruction instruction, InvariantComputer invs) {
      if (DEBUG) {
        System.err.println("visitInvoke: " + instruction);
      }

      PointerKey uniqueCatch = null;
      if (hasUniqueCatchBlock(instruction, ir)) {
        uniqueCatch = getBuilder().getUniqueCatchKey(instruction, ir, node);
      }

      InstanceKey[][] invariantParameters = invs.computeInvariantParameters(instruction);
      if (instruction.getCallSite().isStatic()) {
        for (CGNode n : getBuilder().getTargetsForCall(node, instruction, invariantParameters)) {
          getBuilder().processResolvedCall(node, instruction, n, invariantParameters, uniqueCatch);
          if (DEBUG) {
            System.err.println("visitInvoke class init " + n);
          }

          // side effect of invoke: may call class initializer
          processClassInitializer(n.getMethod().getDeclaringClass());
        }
      } else {
        // Add a side effect that will fire when we determine a value
        // for a dispatch parameter. This side effect will create a new node
        // and new constraints based on the new callee context.
        IntSet params = getBuilder().getContextSelector().getRelevantParameters(node, instruction.getCallSite());
        if (! params.contains(0)) {
          params = IntSetUtil.makeMutableCopy(params);
          ((MutableIntSet)params).add(0);
        }
        final int vns[] = new int[ params.size() ];
        params.foreach(new IntSetAction() {
          private int i = 0;
          public void act(int x) {
            vns[i++] = instruction.getUse(x);
          }
        });
        
        if (contentsAreInvariant(symbolTable, du, vns)) {
          for(CGNode n : getBuilder().getTargetsForCall(node, instruction, invariantParameters)) {
              getBuilder().processResolvedCall(node, instruction, n, invariantParameters, uniqueCatch);
              // side effect of invoke: may call class initializer
              processClassInitializer(n.getMethod().getDeclaringClass());
          }
        } else {
          if (DEBUG) {
            System.err.println("Add side effect, dispatch to " + instruction + " for " + params);
          }
 
          final List pks = new ArrayList(params.size());
          params.foreach(new IntSetAction() {
            public void act(int x) {
              if (!contentsAreInvariant(symbolTable, du, instruction.getUse(x))) {
                pks.add(getBuilder().getPointerKeyForLocal(node, instruction.getUse(x)));
              }
            }
          });
   
          DispatchOperator dispatchOperator = getBuilder().new DispatchOperator(instruction, node,
              invariantParameters, uniqueCatch, params);
          system.newSideEffect(dispatchOperator, pks.toArray(new PointerKey[pks.size()]));
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitNew(com.ibm.wala.ssa.NewInstruction)
     */
    @Override
    public void visitNew(SSANewInstruction instruction) {
      InstanceKey iKey = getInstanceKeyForAllocation(instruction.getNewSite());

        // something went wrong. I hope someone raised a warning.
        return;
      }
      PointerKey def = getPointerKeyForLocal(instruction.getDef());
      IClass klass = iKey.getConcreteType();

      if (DEBUG) {
        System.err.println("visitNew: " + instruction + " " + iKey + " " + system.findOrCreateIndexForInstanceKey(iKey));
      }

      if (klass == null) {
        if (DEBUG) {
          System.err.println("Resolution failure: " + instruction);
        }
        return;
      }

      if (!contentsAreInvariant(symbolTable, du, instruction.getDef())) {
        system.newConstraint(def, iKey);
      } else {
        system.findOrCreateIndexForInstanceKey(iKey);
        system.recordImplicitPointsToSet(def);
      }

      // side effect of new: may call class initializer
      if (DEBUG) {
        System.err.println("visitNew call clinit: " + klass);
      }
      processClassInitializer(klass);

      // add instance keys and pointer keys for array contents
      int dim = 0;
      InstanceKey lastInstance = iKey;
      while (klass != null && klass.isArrayClass()) {
        klass = ((ArrayClass) klass).getElementClass();
        // klass == null means it's a primitive
        if (klass != null && klass.isArrayClass()) {
          if (instruction.getNumberOfUses() <= (dim + 1)) {
            break;
          }
          int sv = instruction.getUse(dim + 1);
          if (ir.getSymbolTable().isIntegerConstant(sv)) {
            Integer c = (Integer) ir.getSymbolTable().getConstantValue(sv);
            if (c.intValue() == 0) {
              break;
            }
          }
          InstanceKey ik = getInstanceKeyForMultiNewArray(instruction.getNewSite(), dim);
          PointerKey pk = getPointerKeyForArrayContents(lastInstance);
          if (DEBUG_MULTINEWARRAY) {
            System.err.println("multinewarray constraint: ");
            System.err.println("   pk: " + pk);
            System.err.println("   ik: " + system.findOrCreateIndexForInstanceKey(ik) + " concrete type " + ik.getConcreteType()
                + " is " + ik);
            System.err.println("   klass:" + klass);
          }
          system.newConstraint(pk, ik);
          lastInstance = ik;
          dim++;
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitThrow(com.ibm.wala.ssa.ThrowInstruction)
     */
    @Override
    public void visitThrow(SSAThrowInstruction instruction) {
      // don't do anything: we handle exceptional edges
      // in a separate pass
    }

    /*
     * @see com.ibm.wala.ssa.Instruction.Visitor#visitGetCaughtException(com.ibm.wala.ssa.GetCaughtExceptionInstruction)
     */
    @Override
    public void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction) {
      List peis = getIncomingPEIs(ir, getBasicBlock());
      PointerKey def = getPointerKeyForLocal(instruction.getDef());
      // SJF: we don't optimize based on dead catch blocks yet ... it's a little
      // tricky due interaction with the SINGLE_USE optimization which directly
      // shoves exceptional return values from calls into exception vars.
      // it may not be worth doing this.
      // if (hasNoInterestingUses(instruction.getDef(), du)) {
      // solver.recordImplicitPointsToSet(def);
      // } else {
      Set types = getCaughtExceptionTypes(instruction, ir);
      getBuilder().addExceptionDefConstraints(ir, du, node, peis, def, types);
      // }
    }

    /**
     * TODO: What is this doing? Document me!
     */
    private int booleanConstantTest(SSAConditionalBranchInstruction c, int v) {
      int result = 0;
      // right for OPR_eq
      if ((symbolTable.isZeroOrFalse(c.getUse(0)) && c.getUse(1) == v)
          || (symbolTable.isZeroOrFalse(c.getUse(1)) && c.getUse(0) == v)) {
        result = -1;
      } else if ((symbolTable.isOneOrTrue(c.getUse(0)) && c.getUse(1) == v)
          || (symbolTable.isOneOrTrue(c.getUse(1)) && c.getUse(0) == v)) {
        result = 1;
      }

      if (c.getOperator() == ConditionalBranchInstruction.Operator.NE) {
        result = -result;
      }

      return result;
    }

    private int nullConstantTest(SSAConditionalBranchInstruction c, int v) {
      if ((symbolTable.isNullConstant(c.getUse(0)) && c.getUse(1) == v)
          || (symbolTable.isNullConstant(c.getUse(1)) && c.getUse(0) == v)) {
        if (c.getOperator() == ConditionalBranchInstruction.Operator.EQ) {
          return 1;
        } else {
          return -1;
        }
      } else {
        return 0;
      }
    }

    @Override
    public void visitPhi(SSAPhiInstruction instruction) {
      if (ir.getMethod() instanceof AbstractRootMethod) {
        PointerKey dst = getPointerKeyForLocal(instruction.getDef());
        if (hasNoInterestingUses(instruction.getDef())) {
          system.recordImplicitPointsToSet(dst);
        } else {
          for (int i = 0; i < instruction.getNumberOfUses(); i++) {
            PointerKey use = getPointerKeyForLocal(instruction.getUse(i));
            if (contentsAreInvariant(symbolTable, du, instruction.getUse(i))) {
              system.recordImplicitPointsToSet(use);
              InstanceKey[] ik = getInvariantContents(instruction.getUse(i));
              for (int j = 0; j < ik.length; j++) {
                system.newConstraint(dst, ik[j]);
              }
            } else {
              system.newConstraint(dst, assignOperator, use);
            }
          }
        }
      }
    }

    /*
     * @see com.ibm.wala.ssa.SSAInstruction.Visitor#visitPi(com.ibm.wala.ssa.SSAPiInstruction)
     */
    @Override
    public void visitPi(SSAPiInstruction instruction) {
      int dir;

      if (hasNoInterestingUses(instruction.getDef())) {
        PointerKey dst = getPointerKeyForLocal(instruction.getDef());
        system.recordImplicitPointsToSet(dst);
      } else {
        ControlFlowGraph cfg = ir.getControlFlowGraph();
        if (com.ibm.wala.cfg.Util.endsWithConditionalBranch(cfg, getBasicBlock()) && cfg.getSuccNodeCount(getBasicBlock()) == 2) {
          SSAConditionalBranchInstruction cond = (SSAConditionalBranchInstruction) com.ibm.wala.cfg.Util.getLastInstruction(cfg,
              getBasicBlock());
          SSAInstruction cause = instruction.getCause();
          BasicBlock target = (BasicBlock) cfg.getNode(instruction.getSuccessor());

          if ((cause instanceof SSAInstanceofInstruction)) {
            int direction = booleanConstantTest(cond, cause.getDef());
            if (direction != 0) {
              TypeReference type = ((SSAInstanceofInstruction) cause).getCheckedType();
              IClass cls = getClassHierarchy().lookupClass(type);
              if (cls == null) {
    
                PointerKey dst = getPointerKeyForLocal(instruction.getDef());
                addPiAssignment(dst, instruction.getVal());
              } else {
                PointerKey dst = getFilteredPointerKeyForLocal(instruction.getDef(), new FilteredPointerKey.SingleClassFilter(cls));
                PointerKey src = getPointerKeyForLocal(instruction.getVal());
                if ((target == com.ibm.wala.cfg.Util.getTakenSuccessor(cfg, getBasicBlock()) && direction == 1)
                    || (target == com.ibm.wala.cfg.Util.getNotTakenSuccessor(cfg, getBasicBlock()) && direction == -1)) {
                  system.newConstraint(dst, getBuilder().filterOperator, src);
                } else {
                  system.newConstraint(dst, getBuilder().inverseFilterOperator, src);
                }
              }
            }
          } else if ((dir = nullConstantTest(cond, instruction.getVal())) != 0) {
            if ((target == com.ibm.wala.cfg.Util.getTakenSuccessor(cfg, getBasicBlock()) && dir == -1)
                || (target == com.ibm.wala.cfg.Util.getNotTakenSuccessor(cfg, getBasicBlock()) && dir == 1)) {
              PointerKey dst = getPointerKeyForLocal(instruction.getDef());
              addPiAssignment(dst, instruction.getVal());
            }
          } else {
            PointerKey dst = getPointerKeyForLocal(instruction.getDef());
            addPiAssignment(dst, instruction.getVal());
          }
        } else {
          PointerKey dst = getPointerKeyForLocal(instruction.getDef());
          addPiAssignment(dst, instruction.getVal());
        }
      }
    }

    /**
     * Add a constraint to the system indicating that the contents of local src flows to dst, with no special type filter.
     */
    private void addPiAssignment(PointerKey dst, int src) {
      PointerKey srcKey = getPointerKeyForLocal(src);
      if (contentsAreInvariant(symbolTable, du, src)) {
        system.recordImplicitPointsToSet(srcKey);
        InstanceKey[] ik = getInvariantContents(src);
        for (int j = 0; j < ik.length; j++) {
          system.newConstraint(dst, ik[j]);
        }
      } else {
        system.newConstraint(dst, assignOperator, srcKey);
      }

    }

    public ISSABasicBlock getBasicBlock() {
      return basicBlock;
    }

    /**
     * The calling loop must call this in each iteration!
     */
    public void setBasicBlock(ISSABasicBlock block) {
      basicBlock = block;
    }

    protected interface InvariantComputer {
 
      InstanceKey[][] computeInvariantParameters(SSAAbstractInvokeInstruction call);

    }

    public class DefaultInvariantComputer implements InvariantComputer {
    /**
     * Side effect: records invariant parameters as implicit points-to-sets.
     * 
     * @return if non-null, then result[i] holds the set of instance keys which may be passed as the ith parameter. (which must be
     *         invariant)
     */
    public InstanceKey[][] computeInvariantParameters(SSAAbstractInvokeInstruction call) {
      InstanceKey[][] constParams = null;
      for (int i = 0; i < call.getNumberOfUses(); i++) {
        // not sure how getUse(i) <= 0 .. dead code?
        // TODO: investigate
        if (call.getUse(i) > 0) {
          if (contentsAreInvariant(symbolTable, du, call.getUse(i))) {
            system.recordImplicitPointsToSet(getPointerKeyForLocal(call.getUse(i)));
            if (constParams == null) {
              constParams = new InstanceKey[call.getNumberOfUses()][];
            }
            constParams[i] = getInvariantContents(call.getUse(i));
            for (int j = 0; j < constParams[i].length; j++) {
              system.findOrCreateIndexForInstanceKey(constParams[i][j]);
            }
          }
        }
      }
      return constParams;
    }
    }
    @Override
    public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
      PointerKey def = getPointerKeyForLocal(instruction.getDef());
      assert instruction.getType() == TypeReference.JavaLangClass;
      InstanceKey iKey = getInstanceKeyForClassObject((TypeReference) instruction.getToken());
      IClass klass = getClassHierarchy().lookupClass((TypeReference) instruction.getToken());
      if (klass != null) {
        processClassInitializer(klass);
      }

      if (!contentsAreInvariant(symbolTable, du, instruction.getDef())) {
        system.newConstraint(def, iKey);
      } else {
        system.findOrCreateIndexForInstanceKey(iKey);
        system.recordImplicitPointsToSet(def);
      }
    }

    /**
     * TODO: lift most of this logic to PropagationCallGraphBuilder
     * 
     * Add a call to the class initializer from the root method.
     */
    private void processClassInitializer(IClass klass) {

      assert klass != null;

      if (!getBuilder().getOptions().getHandleStaticInit()) {
        return;
      }

      if (getBuilder().clinitVisited.contains(klass)) {
        return;
      }
      getBuilder().clinitVisited.add(klass);

      if (klass.getClassInitializer() != null) {
        if (DEBUG) {
          System.err.println("process class initializer for " + klass);
        }

        // add an invocation from the fake root method to the 
        AbstractRootMethod fakeWorldClinitMethod = (AbstractRootMethod) callGraph.getFakeWorldClinitNode().getMethod();
        MethodReference m = klass.getClassInitializer().getReference();
        CallSiteReference site = CallSiteReference.make(1, m, IInvokeInstruction.Dispatch.STATIC);
        IMethod targetMethod = getOptions().getMethodTargetSelector().getCalleeTarget(callGraph.getFakeRootNode(), site, null);
        if (targetMethod != null) {
          CGNode target = getTargetForCall(callGraph.getFakeRootNode(), site, null, null);
          if (target != null && callGraph.getPredNodeCount(target) == 0) {
            SSAAbstractInvokeInstruction s = fakeWorldClinitMethod.addInvocation(new int[0], site);
            PointerKey uniqueCatch = getBuilder().getPointerKeyForExceptionalReturnValue(callGraph.getFakeRootNode());
            getBuilder().processResolvedCall(callGraph.getFakeWorldClinitNode(), s, target, null, uniqueCatch);
          }
        }
      }

      IClass sc = klass.getSuperclass();
      if (sc != null) {
        processClassInitializer(sc);
      }
    }
  }

  /**
   * Add constraints for a call site after we have computed a reachable target for the dispatch
   * 
   * Side effect: add edge to the call graph.
   * 
   * @param instruction
   * @param constParams if non-null, then constParams[i] holds the set of instance keys that are passed as param i, or null if param
   *          i is not invariant
   * @param uniqueCatchKey if non-null, then this is the unique PointerKey that catches all exceptions from this call site.
   */
  @SuppressWarnings("deprecation")
  private void processResolvedCall(CGNode caller, SSAAbstractInvokeInstruction instruction, CGNode target,
      InstanceKey[][] constParams, PointerKey uniqueCatchKey) {

    if (DEBUG) {
      System.err.println("processResolvedCall: " + caller + " ," + instruction + " , " + target);
    }

    if (DEBUG) {
      System.err.println("addTarget: " + caller + " ," + instruction + " , " + target);
    }
    caller.addTarget(instruction.getCallSite(), target);

    if (FakeRootMethod.isFakeRootMethod(caller.getMethod().getReference())) {
      if (entrypointCallSites.contains(instruction.getCallSite())) {
        callGraph.registerEntrypoint(target);
      }
    }

    if (!haveAlreadyVisited(target)) {
      markDiscovered(target);
    }
    
    processCallingConstraints(caller, instruction, target, constParams, uniqueCatchKey);
  }
  
  protected void processCallingConstraints(CGNode caller, SSAAbstractInvokeInstruction instruction, CGNode target,
      InstanceKey[][] constParams, PointerKey uniqueCatchKey) {
    // TODO: i'd like to enable this optimization, but it's a little tricky
    // to recover the implicit points-to sets with recursion. TODO: don't
    // be lazy and code the recursive logic to enable this.
    // if (hasNoInstructions(target)) {
    // // record points-to sets for formals implicitly .. computed on
    // // demand.
    // // TODO: generalize this by using hasNoInterestingUses on parameters.
    // // however .. have to be careful to cache results in that case ... don't
    // // want
    // // to recompute du each time we process a call to Object. !
    // for (int i = 0; i < instruction.getNumberOfUses(); i++) {
    // // we rely on the invariant that the value number for the ith parameter
    // // is i+1
    // final int vn = i + 1;
    // PointerKey formal = getPointerKeyForLocal(target, vn);
    // if (target.getMethod().getParameterType(i).isReferenceType()) {
    // system.recordImplicitPointsToSet(formal);
    // }
    // }
    // } else {
    // generate contraints from parameter passing
    int nUses = instruction.getNumberOfParameters();
    int nExpected = target.getMethod().getNumberOfParameters();

    /*
     * int nExpected = target.getMethod().getReference().getNumberOfParameters(); if (!target.getMethod().isStatic() &&
     * !target.getMethod().isClinit()) { nExpected++; }
     */

    if (nUses != nExpected) {
      // some sort of unverifiable code mismatch. give up.
      return;
    }

    // we're a little sloppy for now ... we don't filter calls to
    // java.lang.Object.
    // TODO: we need much more precise filters than cones in order to handle
    // the various types of dispatch logic. We need a filter that expresses
    // "the set of types s.t. x.foo resolves to y.foo."
    for (int i = 0; i < instruction.getNumberOfParameters(); i++) {
      if (target.getMethod().getParameterType(i).isReferenceType()) {
        PointerKey formal = getTargetPointerKey(target, i);
        if (constParams != null && constParams[i] != null) {
          InstanceKey[] ik = constParams[i];
          for (int j = 0; j < ik.length; j++) {
            system.newConstraint(formal, ik[j]);
          }
        } else {
          if (instruction.getUse(i) < 0) {
            Assertions.UNREACHABLE("unexpected " + instruction + " in " + caller);
          }
          PointerKey actual = getPointerKeyForLocal(caller, instruction.getUse(i));
          if (formal instanceof FilteredPointerKey) {
            system.newConstraint(formal, filterOperator, actual);
          } else {
            system.newConstraint(formal, assignOperator, actual);
          }
        }
      }
    }

    // generate contraints from return value.
    if (instruction.hasDef() && instruction.getDeclaredResultType().isReferenceType()) {
      PointerKey result = getPointerKeyForLocal(caller, instruction.getDef());
      PointerKey ret = getPointerKeyForReturnValue(target);
      system.newConstraint(result, assignOperator, ret);
    }
    // generate constraints from exception return value.
    PointerKey e = getPointerKeyForLocal(caller, instruction.getException());
    PointerKey er = getPointerKeyForExceptionalReturnValue(target);
    if (SHORT_CIRCUIT_SINGLE_USES && uniqueCatchKey != null) {
      // e has exactly one use. so, represent e implicitly
      system.newConstraint(uniqueCatchKey, assignOperator, er);
    } else {
      system.newConstraint(e, assignOperator, er);
    }
    // }
  }

  /**
   * An operator to fire when we discover a potential new callee for a virtual or interface call site.
   * 
   * This operator will create a new callee context and constraints if necessary.
   */
  final class DispatchOperator extends AbstractOperator implements IPointerOperator {
    private final SSAAbstractInvokeInstruction call;

    private final CGNode node;

    private final InstanceKey[][] constParams;

    private final PointerKey uniqueCatch;

    /**
     * relevant parameter indices for the registered {@link ContextSelector}
     * 
     * @see ContextSelector#getRelevantParameters(CGNode, CallSiteReference)
     */
    private final int[] dispatchIndices;
    
    /**
     * The set of instance keys that have already been processed.
     * previousPtrs[i] contains the processed instance keys for parameter
     * position dispatchIndices[i]
     */
    final private MutableIntSet[] previousPtrs;
    
    /**
     * @param call
     * @param node
     * @param constParams if non-null, then constParams[i] holds the String constant that is passed as param i, or null if param i
     *          is not a String constant
     */
    DispatchOperator(SSAAbstractInvokeInstruction call, CGNode node, InstanceKey[][] constParams,
        PointerKey uniqueCatch, IntSet dispatchIndices) {
      this.call = call;
      this.node = node;
      this.constParams = constParams;
      this.uniqueCatch = uniqueCatch;
      this.dispatchIndices = IntSetUtil.toArray(dispatchIndices);
      // we better always be interested in the receiver
      assert this.dispatchIndices[0] == 0;
      previousPtrs = new MutableIntSet[dispatchIndices.size()];
      for(int i = 0; i < previousPtrs.length; i++) {
        previousPtrs[i] = IntSetUtil.getDefaultIntSetFactory().make();
      }
    }



    /*
     * @see com.ibm.wala.dataflow.fixpoint.UnaryOperator#evaluate(com.ibm.wala.dataflow.fixpoint.IVariable,
     * com.ibm.wala.dataflow.fixpoint.IVariable)
     */
    @Override
    public byte evaluate(PointsToSetVariable lhs, final PointsToSetVariable[] rhs) {
      assert dispatchIndices.length >= rhs.length : "bad operator at " + call;
      // did evaluating the dispatch operation add a new possible target
      // to the call site?  
      final MutableBoolean addedNewTarget = new MutableBoolean();
      
      final MutableIntSet receiverVals;
      if (constParams != null && constParams[0] != null) {
        receiverVals = IntSetUtil.make();
        for(InstanceKey ik : constParams[0]) {
          receiverVals.add(system.getInstanceIndex(ik));
        }
      } else {
        receiverVals = rhs[0].getValue();
      }
      
      if (receiverVals == null) {
        // this constraint was put on the work list, probably by
        // initialization,
        // even though the right-hand-side is empty.
        // TODO: be more careful about what goes on the worklist to
        // avoid this.
        if (DEBUG) {
          System.err.println("EVAL dispatch with value null");
        }
        return NOT_CHANGED;        
        
      }
      // we handle the parameter positions one by one, rather than enumerating
      // the cartesian product of possibilities. this disallows
      // context-sensitivity policies like true CPA, but is necessary for
      // performance.
      InstanceKey keys[] = new InstanceKey[constParams == null? dispatchIndices[dispatchIndices.length-1]+1: constParams.length];
      // determine whether we're handling a new receiver; used later
      // to check for redundancy
      boolean newReceiver = !receiverVals.isSubset(previousPtrs[0]);
      // keep separate rhsIndex, since it doesn't advance for constant
      // parameters
      int rhsIndex = (constParams != null && constParams[0] != null)? 0: 1;
      // this flag is set to true if we ever call handleAllReceivers() in the
      // loop below. we need to catch the case where we have a new receiver, but
      // there are no other dispatch indices with new values
      boolean propagatedReceivers = false;
      // we start at index 1 since we need to handle the receiver specially; see
      // below
      for (int index = 1; index < dispatchIndices.length; index++) {
        try {
          MonitorUtil.throwExceptionIfCanceled(monitor);
        } catch (CancelException e) {
          throw new CancelRuntimeException(e);
        }
        int paramIndex = dispatchIndices[index];
        assert keys[paramIndex] == null;
        final MutableIntSet prevAtIndex = previousPtrs[index];
        if (constParams != null && constParams[paramIndex] != null) {
          // we have a constant parameter.  only need to propagate again if we've never done it before or if we have a new receiver
          if (newReceiver || prevAtIndex.isEmpty()) {
            for(int i = 0; i < constParams[paramIndex].length; i++) {
              keys[paramIndex] = constParams[paramIndex][i];
              handleAllReceivers(receiverVals,keys, addedNewTarget);
              propagatedReceivers = true;
              int ii = system.instanceKeys.getMappedIndex(constParams[paramIndex][i]);
              prevAtIndex.add(ii);
            }            
          }
        } else { // non-constant parameter
          PointsToSetVariable v = rhs[rhsIndex];
          if (v.getValue() != null) {
            IntIterator ptrs = v.getValue().intIterator();
            while (ptrs.hasNext()) {
              int ptr = ptrs.next();
              if (newReceiver || !prevAtIndex.contains(ptr)) {
                keys[paramIndex] = system.getInstanceKey(ptr);
                handleAllReceivers(receiverVals,keys, addedNewTarget);
                propagatedReceivers = true;
                prevAtIndex.add(ptr);
              }
            }
          } 
          rhsIndex++;
        }
        keys[paramIndex] = null;
      }
      if (newReceiver) {
        if (!propagatedReceivers) {
          // we have a new receiver value, and it wasn't propagated at all,
          // so propagate it now
          handleAllReceivers(receiverVals, keys, addedNewTarget);
        }
        // update receiver cache
        previousPtrs[0].addAll(receiverVals);
      }

      byte sideEffectMask = addedNewTarget.b ? (byte) SIDE_EFFECT_MASK : 0;
      return (byte) (NOT_CHANGED | sideEffectMask);
    }

    private void handleAllReceivers(MutableIntSet receiverVals, InstanceKey[] keys, MutableBoolean sideEffect) {
      assert keys[0] == null;
      IntIterator receiverIter = receiverVals.intIterator();
      while (receiverIter.hasNext()) {
        final int rcvr = receiverIter.next();
        keys[0] = system.getInstanceKey(rcvr);
        if (clone2Assign) {
          // for efficiency: assume that only call sites that reference
          // clone() might dispatch to clone methods
          if (call.getCallSite().getDeclaredTarget().getSelector().equals(cloneSelector)) {
            IClass recv = (keys[0] != null) ? keys[0].getConcreteType() : null;
            IMethod targetMethod = getOptions().getMethodTargetSelector().getCalleeTarget(node, call.getCallSite(), recv);
            if (targetMethod != null && targetMethod.getReference().equals(CloneInterpreter.CLONE)) {
              // treat this call to clone as an assignment
              PointerKey result = getPointerKeyForLocal(node, call.getDef());
              PointerKey receiver = getPointerKeyForLocal(node, call.getReceiver());
              system.newConstraint(result, assignOperator, receiver);
              return;
            }
          }
        }
        CGNode target = getTargetForCall(node, call.getCallSite(), keys[0].getConcreteType(), keys);
        if (target == null) {
          // This indicates an error; I sure hope getTargetForCall
          // raised a warning about this!
          if (DEBUG) {
            System.err.println("Warning: null target for call " + call);
          }
        } else {
          IntSet targets = getCallGraph().getPossibleTargetNumbers(node, call.getCallSite());
          // even if we've seen this target before, if we have constant
          // parameters, we may need to re-process the call, as the constraints
          // for the first time we reached this target may not have been fully
          // general. TODO a more refined check?
          if (targets != null && targets.contains(target.getGraphNodeId()) && noConstParams()) {
            // do nothing; we've previously discovered and handled this
            // receiver for this call site.
          } else {
            // process the newly discovered target for this call
            sideEffect.b = true;
            processResolvedCall(node, call, target, constParams, uniqueCatch);
            if (!haveAlreadyVisited(target)) {
              markDiscovered(target);
            }
          }
        }
      }
      keys[0] = null;
    }

    private boolean noConstParams() {
      if (constParams != null) {
        for (int i = 0; i < constParams.length; i++) {
          if (constParams[i] != null) {
            for (int j = 0; j < constParams[i].length; i++) {
              if (constParams[i][j] != null) {
                return false;
              }
            }
          }
        }
      }
      return true;
    }



    @Override
    public String toString() {
      return "Dispatch to " + call + " in node " + node;
    }

    @Override
    public int hashCode() {
      return node.hashCode() + 90289 * call.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      // note that these are not necessarily canonical, since
      // with synthetic factories we may regenerate constraints
      // many times. TODO: change processing of synthetic factories
      // so that we guarantee to insert each dispatch equation
      // only once ... if this were true we could optimize this
      // with reference equality

      // instanceof is OK because this class is final
      if (o instanceof DispatchOperator) {
        DispatchOperator other = (DispatchOperator) o;
        return node.equals(other.node) && call.equals(other.call) && Arrays.deepEquals(constParams, other.constParams);
      } else {
        return false;
      }
    }

    /*
     * @see com.ibm.wala.ipa.callgraph.propagation.IPointerOperator#isComplex()
     */
    public boolean isComplex() {
      return true;
    }
  }

  protected void iterateCrossProduct(final CGNode caller, final SSAAbstractInvokeInstruction call, IntSet parameters,
      final InstanceKey[][] invariants, final VoidFunction f) {
    final int params[] = IntSetUtil.toArray(parameters);
    final InstanceKey[] keys = new InstanceKey[call.getNumberOfParameters()];
    final CallSiteReference site = call.getCallSite();
    new Object() {
      private void rec(final int pi) {
        if (pi == params.length) {
          f.apply(keys);
        } else {
          final int p = params[pi];
          int vn = call.getUse(p);
          PointerKey var = getPointerKeyForLocal(caller, vn);
          InstanceKey[] ik = invariants != null ? invariants[p] : null;
          if (ik != null) {
            if (ik.length > 0) {
              for (int i = 0; i < ik.length; i++) {
                system.findOrCreateIndexForInstanceKey(ik[i]);
                keys[p] = ik[i];
                rec(pi + 1);
              }
            } else {
   * 
              if (!site.isDispatch() || p != 0) {
                keys[p] = null;
                rec(pi + 1);
              }
            }
          } else {
            IntSet s = system.findOrCreatePointsToSet(var).getValue();
            if (s != null && !s.isEmpty()) {
              s.foreach(new IntSetAction() {
                public void act(int x) {
                  keys[p] = system.getInstanceKey(x);
                  rec(pi + 1);
                }
              });
            } else {
              if (!site.isDispatch() || p != 0) {
                keys[p] = null;
                rec(pi + 1);
              }
            }
          }
        }
      }
    }.rec(0);
  }
  
  protected Set getTargetsForCall(final CGNode caller, final SSAAbstractInvokeInstruction instruction, InstanceKey[][] invs) {
    // This method used to take a CallSiteReference as a parameter, rather than
    // an SSAAbstractInvokeInstruction. This was bad, since it's
    // possible for multiple invoke instructions with different actual
    // parameters to be associated with a single CallSiteReference. Changed
    // to take the invoke instruction as a parameter instead, since invs is
    // associated with the instruction
    final CallSiteReference site = instruction.getCallSite();
    IntSet params = contextSelector.getRelevantParameters(caller, site);
    if (!site.isStatic() && !params.contains(0)) {
      params = IntSetUtil.makeMutableCopy(params);
      ((MutableIntSet)params).add(0);
    }
    final Set targets = HashSetFactory.make();
    VoidFunction f = new VoidFunction() {
      public void apply(InstanceKey[] v) {
        IClass recv = null;
        if (site.isDispatch()) {
          recv = v[0].getConcreteType();
        }
        CGNode target = getTargetForCall(caller, site, recv, v);
        if (target != null) {
          targets.add(target);
        }
      }
    };
    iterateCrossProduct(caller, instruction, params, invs, f);
     return targets;
  }

  public boolean hasNoInterestingUses(CGNode node, int vn, DefUse du) {

    if (du == null) {
      throw new IllegalArgumentException("du is null");
    }
    if (vn <= 0) {
      throw new IllegalArgumentException("v is invalid: " + vn);
    }
    // todo: enhance this by solving a dead-code elimination
    // problem.
    InterestingVisitor v = makeInterestingVisitor(node, vn);
    for (Iterator it = du.getUses(v.vn); it.hasNext();) {
      SSAInstruction s = (SSAInstruction) it.next();
      s.visit(v);
      if (v.bingo) {
        return false;
      }
    }
    return true;
  }

  protected InterestingVisitor makeInterestingVisitor(CGNode node, int vn) {
    return new InterestingVisitor(vn);
  }

  /**
   * sets bingo to true when it visits an interesting instruction
   */
  protected static class InterestingVisitor extends SSAInstruction.Visitor {
    protected final int vn;

    protected InterestingVisitor(int vn) {
      this.vn = vn;
    }

    protected boolean bingo = false;

    @Override
    public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
      if (!instruction.typeIsPrimitive() && instruction.getArrayRef() == vn) {
        bingo = true;
      }
    }

    @Override
    public void visitArrayStore(SSAArrayStoreInstruction instruction) {
      if (!instruction.typeIsPrimitive() && (instruction.getArrayRef() == vn || instruction.getValue() == vn)) {
        bingo = true;
      }
    }

    @Override
    public void visitCheckCast(SSACheckCastInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitGet(SSAGetInstruction instruction) {
      FieldReference field = instruction.getDeclaredField();
      if (!field.getFieldType().isPrimitiveType()) {
        bingo = true;
      }
    }

    @Override
    public void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitInvoke(SSAInvokeInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitPhi(SSAPhiInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitPi(SSAPiInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitPut(SSAPutInstruction instruction) {
      FieldReference field = instruction.getDeclaredField();
      if (!field.getFieldType().isPrimitiveType()) {
        bingo = true;
      }
    }

    @Override
    public void visitReturn(SSAReturnInstruction instruction) {
      bingo = true;
    }

    @Override
    public void visitThrow(SSAThrowInstruction instruction) {
      bingo = true;
    }
  }

  /**
   * TODO: enhance this logic using type inference
   * 
   * @param instruction
   * @return true if we need to filter the receiver type to account for virtual dispatch
   */
  @SuppressWarnings("unused")
  private boolean needsFilterForReceiver(SSAAbstractInvokeInstruction instruction, CGNode target) {

    FilteredPointerKey.TypeFilter f = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.PARAMETERS[0]);

    if (f != null) {
      // the context selects a particular concrete type for the receiver.
      // we need to filter, unless the declared receiver type implies the
      // concrete type (TODO: need to implement this optimization)
      return true;
    }

    // don't need to filter for invokestatic
    if (instruction.getCallSite().isStatic() || instruction.getCallSite().isSpecial()) {
      return false;
    }

    MethodReference declaredTarget = instruction.getDeclaredTarget();
    IMethod resolvedTarget = getClassHierarchy().resolveMethod(declaredTarget);
    if (resolvedTarget == null) {
      // there's some problem that will be flagged as a warning
      return true;
    }

    return true;
  }

  private boolean isRootType(IClass klass) {
    return klass.getClassHierarchy().isRootClass(klass);
  }

  @SuppressWarnings("unused")
  private boolean isRootType(FilteredPointerKey.TypeFilter filter) {
    if (filter instanceof FilteredPointerKey.SingleClassFilter) {
      return isRootType(((FilteredPointerKey.SingleClassFilter) filter).getConcreteType());
    } else {
      return false;
    }
  }

  /**
   * TODO: enhance this logic using type inference TODO!!!: enhance filtering to consider concrete types, not just cones.
   * precondition: needs Filter
   * @param target
   * @return an IClass which represents
   */
  protected PointerKey getTargetPointerKey(CGNode target, int index) {
    int vn;
    if (target.getIR() != null) {
      vn = target.getIR().getSymbolTable().getParameter(index);
    } else {
      vn = index+1;
    }

    FilteredPointerKey.TypeFilter filter = (FilteredPointerKey.TypeFilter) target.getContext().get(ContextKey.PARAMETERS[index]);
    if (filter != null && !filter.isRootFilter()) {
        return pointerKeyFactory.getFilteredPointerKeyForLocal(target, vn, filter);
    
    } else if (index == 0 && !target.getMethod().isStatic()) {
      // the context does not select a particular concrete type for the
      // receiver, so use the type of the method
      IClass C = getReceiverClass(target.getMethod());
      if (C.getClassHierarchy().getRootClass().equals(C)) {
        return pointerKeyFactory.getPointerKeyForLocal(target, vn);        
      } else {
        return pointerKeyFactory.getFilteredPointerKeyForLocal(target, vn, new FilteredPointerKey.SingleClassFilter(C));
      }
      
    } else {
      return pointerKeyFactory.getPointerKeyForLocal(target, vn);
    }
  }

  /**
   * @param method
   * @return the receiver class for this method.
   */
  private IClass getReceiverClass(IMethod method) {
    TypeReference formalType = method.getParameterType(0);
    IClass C = getClassHierarchy().lookupClass(formalType);
    if (method.isStatic()) {
      Assertions.UNREACHABLE("asked for receiver of static method " + method);
    }
    if (C == null) {
      Assertions.UNREACHABLE("no class found for " + formalType + " recv of " + method);
    }
    return C;
  }

  /**
   * A value is "invariant" if we can figure out the instances it can ever point to locally, without resorting to propagation.
   * 
   * @param valueNumber
   * @return true iff the contents of the local with this value number can be deduced locally, without propagation
   */
  protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumber) {
    if (isConstantRef(symbolTable, valueNumber)) {
      return true;
    } else if (SHORT_CIRCUIT_INVARIANT_SETS) {
      SSAInstruction def = du.getDef(valueNumber);
      if (def instanceof SSANewInstruction) {
        return true;
      } else {
        return false;
      }
    } else {
      return false;
    }
  }

  protected boolean contentsAreInvariant(SymbolTable symbolTable, DefUse du, int valueNumbers[]) {
    for(int i = 0; i < valueNumbers.length; i++) {
      if (! contentsAreInvariant(symbolTable, du, valueNumbers[i])) {
        return false;
      }
    }
    return true;
  }
  
  /**
   * precondition:contentsAreInvariant(valueNumber)
   * 
   * @param valueNumber
   * @return the complete set of instances that the local with vn=valueNumber may point to.
   */
  protected InstanceKey[] getInvariantContents(SymbolTable symbolTable, DefUse du, CGNode node, int valueNumber, HeapModel hm) {
    return getInvariantContents(symbolTable, du, node, valueNumber, hm, false);
  }

  protected InstanceKey[] getInvariantContents(SymbolTable symbolTable, DefUse du, CGNode node, int valueNumber, HeapModel hm,
      boolean ensureIndexes) {
    InstanceKey[] result;
    if (isConstantRef(symbolTable, valueNumber)) {
      Object x = symbolTable.getConstantValue(valueNumber);
      if (x instanceof String) {
        // this is always the case in Java. use strong typing in the call to getInstanceKeyForConstant.
        String S = (String) x;
        TypeReference type = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getConstantType(S);
        if (type == null) {
          return new InstanceKey[0];
        }
        InstanceKey ik = hm.getInstanceKeyForConstant(type, S);
        if (ik != null) {
          result = new InstanceKey[] { ik };
        } else {
          result = new InstanceKey[0];
        }
      } else {
        // some non-built in type (e.g. Integer). give up on strong typing.
        // language-specific subclasses (e.g. Javascript) should override this method to get strong typing
        // with generics if desired.
        TypeReference type = node.getMethod().getDeclaringClass().getClassLoader().getLanguage().getConstantType(x);
        if (type == null) {
          return new InstanceKey[0];
        }
        InstanceKey ik = hm.getInstanceKeyForConstant(type, x);
        if (ik != null) {
          result = new InstanceKey[] { ik };
        } else {
          result = new InstanceKey[0];
        }
      }
    } else {
      SSANewInstruction def = (SSANewInstruction) du.getDef(valueNumber);
      InstanceKey iKey = hm.getInstanceKeyForAllocation(node, def.getNewSite());
      result = (iKey == null) ? new InstanceKey[0] : new InstanceKey[] { iKey };
    }

    if (ensureIndexes) {
      for (int i = 0; i < result.length; i++) {
        system.findOrCreateIndexForInstanceKey(result[i]);
      }
    }

    return result;
  }

  protected boolean isConstantRef(SymbolTable symbolTable, int valueNumber) {
    if (valueNumber == -1) {
      return false;
    }
    if (symbolTable.isConstant(valueNumber)) {
      Object v = symbolTable.getConstantValue(valueNumber);
      return (!(v instanceof Number));
    } else {
      return false;
    }
  }

  /**
   * @author sfink
   * 
   *         A warning for when we fail to resolve the type for a checkcast
   */
  private static class CheckcastFailure extends Warning {

    final TypeReference type;

    CheckcastFailure(TypeReference type) {
      super(Warning.SEVERE);
      this.type = type;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + type;
    }

    public static CheckcastFailure create(TypeReference type) {
      return new CheckcastFailure(type);
    }
  }

  /**
   * @author sfink
   * 
   *         A warning for when we fail to resolve the type for a field
   */
  private static class FieldResolutionFailure extends Warning {

    final FieldReference field;

    FieldResolutionFailure(FieldReference field) {
      super(Warning.SEVERE);
      this.field = field;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + field;
    }

    public static FieldResolutionFailure create(FieldReference field) {
      return new FieldResolutionFailure(field);
    }
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.HeapModel#iteratePointerKeys()
   */
  public Iterator iteratePointerKeys() {
    return system.iteratePointerKeys();
  }

  public static Set getCaughtExceptionTypes(SSAGetCaughtExceptionInstruction instruction, IR ir) {
    if (ir == null) {
      throw new IllegalArgumentException("ir is null");
    }
    if (instruction == null) {
      throw new IllegalArgumentException("instruction is null");
    }
    Iterator exceptionTypes = ((ExceptionHandlerBasicBlock) ir.getControlFlowGraph().getNode(
        instruction.getBasicBlockNumber())).getCaughtExceptionTypes();
    HashSet types = HashSetFactory.make(10);
    for (; exceptionTypes.hasNext();) {
      IClass c = ir.getMethod().getClassHierarchy().lookupClass(exceptionTypes.next());
      if (c != null) {
        types.add(c);
      }
    }
    return types;
  }

  public InstanceKey getInstanceKeyForPEI(CGNode node, ProgramCounter instr, TypeReference type) {
    return getInstanceKeyForPEI(node, instr, type, instanceKeyFactory);
  }

  /*
   * @see com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder#makeSolver()
   */
  @Override
  protected IPointsToSolver makeSolver() {
    return new StandardSolver(system, this);
    // return usePreTransitiveSolver ? (IPointsToSolver) new PreTransitiveSolver(system, this) : new StandardSolver(system, this);
    // return true ? (IPointsToSolver)new PreTransitiveSolver(system,this) : new
    // StandardSolver(system,this);
  }
}
File
SSAPropagationCallGraphBuilder.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.cfg;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.util.collections.Filter;
import com.ibm.wala.util.collections.FilterIterator;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.graph.AbstractNumberedGraph;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.NumberedEdgeManager;
import com.ibm.wala.util.graph.NumberedNodeManager;
import com.ibm.wala.util.graph.impl.GraphInverter;
import com.ibm.wala.util.graph.traverse.DFS;
import com.ibm.wala.util.intset.BitVector;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;

/**
 * A pruned view of a {@link ControlFlowGraph}. Use this class along with an {@link EdgeFilter} to produce a custom view of a CFG.
 * 
 * For example, you can use this class to produce a CFG view that ignores certain types of exceptional edges.
 */
public class PrunedCFG> extends AbstractNumberedGraph implements ControlFlowGraph {

  /**
   * @param cfg the original CFG that you want a view of
   * @param filter an object that selectively filters edges in the original CFG
   * @return a view of cfg that includes only edges accepted by the filter.
   * @throws IllegalArgumentException if cfg is null
   */
  public static > PrunedCFG make(final ControlFlowGraph cfg, final EdgeFilter filter) {
    if (cfg == null) {
      throw new IllegalArgumentException("cfg is null");
    }
    return new PrunedCFG(cfg, filter);
  }

  private static class FilteredCFGEdges> implements NumberedEdgeManager {
    private final ControlFlowGraph cfg;

    private final NumberedNodeManager currentCFGNodes;

    private final EdgeFilter filter;

    FilteredCFGEdges(ControlFlowGraph cfg, NumberedNodeManager currentCFGNodes, EdgeFilter filter) {
      this.cfg = cfg;
      this.filter = filter;
      this.currentCFGNodes = currentCFGNodes;
    }

    public Iterator getExceptionalSuccessors(final T N) {
      return new FilterIterator(cfg.getExceptionalSuccessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasExceptionalEdge(N, o);
        }
      });
    }

    public Iterator getNormalSuccessors(final T N) {
      return new FilterIterator(cfg.getNormalSuccessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasNormalEdge(N, o);
        }
      });
    }

    public Iterator getExceptionalPredecessors(final T N) {
      return new FilterIterator(cfg.getExceptionalPredecessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasExceptionalEdge(o, N);
        }
      });
    }

    public Iterator getNormalPredecessors(final T N) {
      return new FilterIterator(cfg.getNormalPredecessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasNormalEdge(o, N);
        }
      });
    }

    public Iterator getSuccNodes(final T N) {
      return new FilterIterator(cfg.getSuccNodes(N), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && (filter.hasNormalEdge(N, o) || filter.hasExceptionalEdge(N, o));
        }
      });
    }

    public int getSuccNodeCount(T N) {
      return Iterator2Collection.toSet(getSuccNodes(N)).size();
    }

    public IntSet getSuccNodeNumbers(T N) {
      MutableIntSet bits = IntSetUtil.make();
      for (Iterator EE = getSuccNodes(N); EE.hasNext();) {
        bits.add(EE.next().getNumber());
      }

      return bits;
    }

    public Iterator getPredNodes(final T N) {
      return new FilterIterator(cfg.getPredNodes(N), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && (filter.hasNormalEdge(o, N) || filter.hasExceptionalEdge(o, N));
        }
      });
    }

    public int getPredNodeCount(T N) {
      return Iterator2Collection.toSet(getPredNodes(N)).size();
    }

    public IntSet getPredNodeNumbers(T N) {
      MutableIntSet bits = IntSetUtil.make();
      for (Iterator EE = getPredNodes(N); EE.hasNext();) {
        bits.add(EE.next().getNumber());
      }

      return bits;
    }

    public boolean hasEdge(T src, T dst) {
      for (Iterator EE = getSuccNodes(src); EE.hasNext();) {
        if (EE.next().equals(dst)) {
          return true;
        }
      }

      return false;
    }

    public void addEdge(T src, T dst) {
      throw new UnsupportedOperationException();
    }

    public void removeEdge(T src, T dst) {
      throw new UnsupportedOperationException();
    }

    public void removeAllIncidentEdges(T node) {
      throw new UnsupportedOperationException();
    }

    public void removeIncomingEdges(T node) {
      throw new UnsupportedOperationException();
    }

    public void removeOutgoingEdges(T node) {
      throw new UnsupportedOperationException();
    }
  }

  private static class FilteredNodes implements NumberedNodeManager {
    private final NumberedNodeManager nodes;

    private final Set subset;

    FilteredNodes(NumberedNodeManager nodes, Set subset) {
      this.nodes = nodes;
      this.subset = subset;
    }

    public int getNumber(T N) {
      if (subset.contains(N))
        return nodes.getNumber(N);
      else
        return -1;
    }

    public T getNode(int number) {
      T N = nodes.getNode(number);
      if (subset.contains(N))
        return N;
      else
        throw new NoSuchElementException();
    }

    public int getMaxNumber() {
      int max = -1;
      for (Iterator NS = nodes.iterator(); NS.hasNext();) {
        T N = NS.next();
        if (subset.contains(N) && getNumber(N) > max) {
          max = getNumber(N);
        }
      }

      return max;
    }

    private Iterator filterNodes(Iterator nodeIterator) {
      return new FilterIterator(nodeIterator, new Filter() {
        public boolean accepts(Object o) {
          return subset.contains(o);
        }
      });
    }

    public Iterator iterateNodes(IntSet s) {
      return filterNodes(nodes.iterateNodes(s));
    }

    public Iterator iterator() {
      return filterNodes(nodes.iterator());
    }

    public int getNumberOfNodes() {
      return subset.size();
    }

    public void addNode(T n) {
      throw new UnsupportedOperationException();
    }

    public void removeNode(T n) {
      throw new UnsupportedOperationException();
    }

    public boolean containsNode(T N) {
      return subset.contains(N);
    }
  }

  private final ControlFlowGraph cfg;

  private final FilteredNodes nodes;

  private final FilteredCFGEdges edges;

  private PrunedCFG(final ControlFlowGraph cfg, final EdgeFilter filter) {
    this.cfg = cfg;
    Graph temp = new AbstractNumberedGraph() {
      private final NumberedEdgeManager edges = new FilteredCFGEdges(cfg, cfg, filter);

      @Override
      protected NumberedNodeManager getNodeManager() {
        return cfg;
      }

      @Override
      protected NumberedEdgeManager getEdgeManager() {
        return edges;
      }
    };

    Set reachable = DFS.getReachableNodes(temp, Collections.singleton(cfg.entry()));
    Set back = DFS.getReachableNodes(GraphInverter.invert(temp), Collections.singleton(cfg.exit()));
    reachable.retainAll(back);
/** BEGIN Custom change: entry and exit in every cfg */
    reachable.add(cfg.entry());
    reachable.add(cfg.exit());
/** END Custom change: entry and exit in every cfg */
        
    this.nodes = new FilteredNodes(cfg, reachable);
    this.edges = new FilteredCFGEdges(cfg, nodes, filter);
  }

  @Override
  protected NumberedNodeManager getNodeManager() {
    return nodes;
  }

  @Override
  protected NumberedEdgeManager getEdgeManager() {
    return edges;
  }

  public List getExceptionalSuccessors(final T N) {
    ArrayList result = new ArrayList();
    for (Iterator it = edges.getExceptionalSuccessors(N); it.hasNext();) {
      result.add(it.next());
    }
    return result;
  }

  public Collection getNormalSuccessors(final T N) {
    return Iterator2Collection.toSet(edges.getNormalSuccessors(N));
  }

  public Collection getExceptionalPredecessors(final T N) {
    return Iterator2Collection.toSet(edges.getExceptionalPredecessors(N));
  }

  public Collection getNormalPredecessors(final T N) {
    return Iterator2Collection.toSet(edges.getNormalPredecessors(N));
  }

  public T entry() {
    return cfg.entry();
  }

  public T exit() {
    return cfg.exit();
  }

  public T getBlockForInstruction(int index) {
    return cfg.getBlockForInstruction(index);
  }

  public I[] getInstructions() {
    return cfg.getInstructions();
  }

  public int getProgramCounter(int index) {
    return cfg.getProgramCounter(index);
  }

  public IMethod getMethod() {
    return cfg.getMethod();
  }

  public BitVector getCatchBlocks() {
    BitVector result = new BitVector();
    BitVector blocks = cfg.getCatchBlocks();
    int i = 0;
    while ((i = blocks.nextSetBit(i)) != -1) {
      if (nodes.containsNode(getNode(i))) {
        result.set(i);
      }
    }

    return result;
  }

  public IntSet getPhiIndices(T bb) {
    assert containsNode(bb);
    assert cfg.containsNode(bb);

    int i = 0;
    MutableIntSet valid = IntSetUtil.make();
    for (Iterator pbs = cfg.getPredNodes(bb); pbs.hasNext(); i++) {
      if (nodes.containsNode(pbs.next())) {
        valid.add(i);
      }
    }

    return valid;
  }

}
=======
/******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.cfg;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.util.collections.Filter;
import com.ibm.wala.util.collections.FilterIterator;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.graph.AbstractNumberedGraph;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.NumberedEdgeManager;
import com.ibm.wala.util.graph.NumberedNodeManager;
import com.ibm.wala.util.graph.impl.GraphInverter;
import com.ibm.wala.util.graph.traverse.DFS;
import com.ibm.wala.util.intset.BitVector;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;

/**
 * A pruned view of a {@link ControlFlowGraph}. Use this class along with an {@link EdgeFilter} to produce a custom view of a CFG.
 * 
 * For example, you can use this class to produce a CFG view that ignores certain types of exceptional edges.
 */
public class PrunedCFG> extends AbstractNumberedGraph implements ControlFlowGraph {

  /**
   * @param cfg the original CFG that you want a view of
   * @param filter an object that selectively filters edges in the original CFG
   * @return a view of cfg that includes only edges accepted by the filter.
   * @throws IllegalArgumentException if cfg is null
   */
  public static > PrunedCFG make(final ControlFlowGraph cfg, final EdgeFilter filter) {
    if (cfg == null) {
      throw new IllegalArgumentException("cfg is null");
    }
    return new PrunedCFG(cfg, filter);
  }

  private static class FilteredCFGEdges> implements NumberedEdgeManager {
    private final ControlFlowGraph cfg;

    private final NumberedNodeManager currentCFGNodes;

    private final EdgeFilter filter;

    FilteredCFGEdges(ControlFlowGraph cfg, NumberedNodeManager currentCFGNodes, EdgeFilter filter) {
      this.cfg = cfg;
      this.filter = filter;
      this.currentCFGNodes = currentCFGNodes;
    }

    public Iterator getExceptionalSuccessors(final T N) {
      return new FilterIterator(cfg.getExceptionalSuccessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasExceptionalEdge(N, o);
        }
      });
    }

    public Iterator getNormalSuccessors(final T N) {
      return new FilterIterator(cfg.getNormalSuccessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasNormalEdge(N, o);
        }
      });
    }

    public Iterator getExceptionalPredecessors(final T N) {
      return new FilterIterator(cfg.getExceptionalPredecessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasExceptionalEdge(o, N);
        }
      });
    }

    public Iterator getNormalPredecessors(final T N) {
      return new FilterIterator(cfg.getNormalPredecessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasNormalEdge(o, N);
        }
      });
    }

    public Iterator getSuccNodes(final T N) {
      return new FilterIterator(cfg.getSuccNodes(N), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && (filter.hasNormalEdge(N, o) || filter.hasExceptionalEdge(N, o));
        }
      });
    }

    public int getSuccNodeCount(T N) {
      return Iterator2Collection.toSet(getSuccNodes(N)).size();
    }

    public IntSet getSuccNodeNumbers(T N) {
      MutableIntSet bits = IntSetUtil.make();
      for (Iterator EE = getSuccNodes(N); EE.hasNext();) {
        bits.add(EE.next().getNumber());
      }

      return bits;
    }

    public Iterator getPredNodes(final T N) {
      return new FilterIterator(cfg.getPredNodes(N), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && (filter.hasNormalEdge(o, N) || filter.hasExceptionalEdge(o, N));
        }
      });
    }

    public int getPredNodeCount(T N) {
      return Iterator2Collection.toSet(getPredNodes(N)).size();
    }

    public IntSet getPredNodeNumbers(T N) {
      MutableIntSet bits = IntSetUtil.make();
      for (Iterator EE = getPredNodes(N); EE.hasNext();) {
        bits.add(EE.next().getNumber());
      }

      return bits;
    }

    public boolean hasEdge(T src, T dst) {
      for (Iterator EE = getSuccNodes(src); EE.hasNext();) {
        if (EE.next().equals(dst)) {
          return true;
        }
      }

      return false;
    }

    public void addEdge(T src, T dst) {
      throw new UnsupportedOperationException();
    }

    public void removeEdge(T src, T dst) {
      throw new UnsupportedOperationException();
    }

    public void removeAllIncidentEdges(T node) {
      throw new UnsupportedOperationException();
    }

    public void removeIncomingEdges(T node) {
      throw new UnsupportedOperationException();
    }

    public void removeOutgoingEdges(T node) {
      throw new UnsupportedOperationException();
    }
  }

  private static class FilteredNodes implements NumberedNodeManager {
    private final NumberedNodeManager nodes;

    private final Set subset;

    FilteredNodes(NumberedNodeManager nodes, Set subset) {
      this.nodes = nodes;
      this.subset = subset;
    }

    public int getNumber(T N) {
      if (subset.contains(N))
        return nodes.getNumber(N);
      else
        return -1;
    }

    public T getNode(int number) {
      T N = nodes.getNode(number);
      if (subset.contains(N))
        return N;
      else
        throw new NoSuchElementException();
    }

    public int getMaxNumber() {
      int max = -1;
      for (Iterator NS = nodes.iterator(); NS.hasNext();) {
        T N = NS.next();
        if (subset.contains(N) && getNumber(N) > max) {
          max = getNumber(N);
        }
      }

      return max;
    }

    private Iterator filterNodes(Iterator nodeIterator) {
      return new FilterIterator(nodeIterator, new Filter() {
        public boolean accepts(Object o) {
          return subset.contains(o);
        }
      });
    }

    public Iterator iterateNodes(IntSet s) {
      return filterNodes(nodes.iterateNodes(s));
    }

    public Iterator iterator() {
      return filterNodes(nodes.iterator());
    }

    public int getNumberOfNodes() {
      return subset.size();
    }

    public void addNode(T n) {
      throw new UnsupportedOperationException();
    }

    public void removeNode(T n) {
      throw new UnsupportedOperationException();
    }

    public boolean containsNode(T N) {
      return subset.contains(N);
    }
  }

  private final ControlFlowGraph cfg;

  private final FilteredNodes nodes;

  private final FilteredCFGEdges edges;

  private PrunedCFG(final ControlFlowGraph cfg, final EdgeFilter filter) {
    this.cfg = cfg;
    Graph temp = new AbstractNumberedGraph() {
      private final NumberedEdgeManager edges = new FilteredCFGEdges(cfg, cfg, filter);

      @Override
      protected NumberedNodeManager getNodeManager() {
        return cfg;
      }

      @Override
      protected NumberedEdgeManager getEdgeManager() {
        return edges;
      }
    };

    Set reachable = DFS.getReachableNodes(temp, Collections.singleton(cfg.entry()));
    Set back = DFS.getReachableNodes(GraphInverter.invert(temp), Collections.singleton(cfg.exit()));
    reachable.retainAll(back);

    this.nodes = new FilteredNodes(cfg, reachable);
    this.edges = new FilteredCFGEdges(cfg, nodes, filter);
  }

  @Override
  protected NumberedNodeManager getNodeManager() {
    return nodes;
  }

  @Override
  protected NumberedEdgeManager getEdgeManager() {
    return edges;
  }

  public List getExceptionalSuccessors(final T N) {
    ArrayList result = new ArrayList();
    for (Iterator it = edges.getExceptionalSuccessors(N); it.hasNext();) {
      result.add(it.next());
    }
    return result;
  }

  public Collection getNormalSuccessors(final T N) {
    return Iterator2Collection.toSet(edges.getNormalSuccessors(N));
  }

  public Collection getExceptionalPredecessors(final T N) {
    return Iterator2Collection.toSet(edges.getExceptionalPredecessors(N));
  }

  public Collection getNormalPredecessors(final T N) {
    return Iterator2Collection.toSet(edges.getNormalPredecessors(N));
  }

  public T entry() {
    return cfg.entry();
  }

  public T exit() {
    return cfg.exit();
  }

  public T getBlockForInstruction(int index) {
    return cfg.getBlockForInstruction(index);
  }

  public I[] getInstructions() {
    return cfg.getInstructions();
  }

  public int getProgramCounter(int index) {
    return cfg.getProgramCounter(index);
  }

  public IMethod getMethod() {
    return cfg.getMethod();
  }

  public BitVector getCatchBlocks() {
    BitVector result = new BitVector();
    BitVector blocks = cfg.getCatchBlocks();
    int i = 0;
    while ((i = blocks.nextSetBit(i)) != -1) {
      if (nodes.containsNode(getNode(i))) {
        result.set(i);
      }
    }

    return result;
  }

  public IntSet getPhiIndices(T bb) {
    assert containsNode(bb);
    assert cfg.containsNode(bb);

    int i = 0;
    MutableIntSet valid = IntSetUtil.make();
    for (Iterator pbs = cfg.getPredNodes(bb); pbs.hasNext(); i++) {
      if (nodes.containsNode(pbs.next())) {
        valid.add(i);
      }
    }

    return valid;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.cfg;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.util.collections.Filter;
import com.ibm.wala.util.collections.FilterIterator;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.graph.AbstractNumberedGraph;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.NumberedEdgeManager;
import com.ibm.wala.util.graph.NumberedNodeManager;
import com.ibm.wala.util.graph.impl.GraphInverter;
import com.ibm.wala.util.graph.traverse.DFS;
import com.ibm.wala.util.intset.BitVector;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetUtil;
import com.ibm.wala.util.intset.MutableIntSet;

/**
 * A pruned view of a {@link ControlFlowGraph}. Use this class along with an {@link EdgeFilter} to produce a custom view of a CFG.
 * 
 * For example, you can use this class to produce a CFG view that ignores certain types of exceptional edges.
 */
public class PrunedCFG> extends AbstractNumberedGraph implements ControlFlowGraph {

  /**
   * @param cfg the original CFG that you want a view of
   * @param filter an object that selectively filters edges in the original CFG
   * @return a view of cfg that includes only edges accepted by the filter.
   * @throws IllegalArgumentException if cfg is null
   */
  public static > PrunedCFG make(final ControlFlowGraph cfg, final EdgeFilter filter) {
    if (cfg == null) {
      throw new IllegalArgumentException("cfg is null");
    }
    return new PrunedCFG(cfg, filter);
  }

  private static class FilteredCFGEdges> implements NumberedEdgeManager {
    private final ControlFlowGraph cfg;

    private final NumberedNodeManager currentCFGNodes;

    private final EdgeFilter filter;

    FilteredCFGEdges(ControlFlowGraph cfg, NumberedNodeManager currentCFGNodes, EdgeFilter filter) {
      this.cfg = cfg;
      this.filter = filter;
      this.currentCFGNodes = currentCFGNodes;
    }

    public Iterator getExceptionalSuccessors(final T N) {
      return new FilterIterator(cfg.getExceptionalSuccessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasExceptionalEdge(N, o);
        }
      });
    }

    public Iterator getNormalSuccessors(final T N) {
      return new FilterIterator(cfg.getNormalSuccessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasNormalEdge(N, o);
        }
      });
    }

    public Iterator getExceptionalPredecessors(final T N) {
      return new FilterIterator(cfg.getExceptionalPredecessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasExceptionalEdge(o, N);
        }
      });
    }

    public Iterator getNormalPredecessors(final T N) {
      return new FilterIterator(cfg.getNormalPredecessors(N).iterator(), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && filter.hasNormalEdge(o, N);
        }
      });
    }

    public Iterator getSuccNodes(final T N) {
      return new FilterIterator(cfg.getSuccNodes(N), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && (filter.hasNormalEdge(N, o) || filter.hasExceptionalEdge(N, o));
        }
      });
    }

    public int getSuccNodeCount(T N) {
      return Iterator2Collection.toSet(getSuccNodes(N)).size();
    }

    public IntSet getSuccNodeNumbers(T N) {
      MutableIntSet bits = IntSetUtil.make();
      for (Iterator EE = getSuccNodes(N); EE.hasNext();) {
        bits.add(EE.next().getNumber());
      }

      return bits;
    }

    public Iterator getPredNodes(final T N) {
      return new FilterIterator(cfg.getPredNodes(N), new Filter() {
        public boolean accepts(T o) {
          return currentCFGNodes.containsNode(o) && (filter.hasNormalEdge(o, N) || filter.hasExceptionalEdge(o, N));
        }
      });
    }

    public int getPredNodeCount(T N) {
      return Iterator2Collection.toSet(getPredNodes(N)).size();
    }

    public IntSet getPredNodeNumbers(T N) {
      MutableIntSet bits = IntSetUtil.make();
      for (Iterator EE = getPredNodes(N); EE.hasNext();) {
        bits.add(EE.next().getNumber());
      }

      return bits;
    }

    public boolean hasEdge(T src, T dst) {
      for (Iterator EE = getSuccNodes(src); EE.hasNext();) {
        if (EE.next().equals(dst)) {
          return true;
        }
      }

      return false;
    }

    public void addEdge(T src, T dst) {
      throw new UnsupportedOperationException();
    }

    public void removeEdge(T src, T dst) {
      throw new UnsupportedOperationException();
    }

    public void removeAllIncidentEdges(T node) {
      throw new UnsupportedOperationException();
    }

    public void removeIncomingEdges(T node) {
      throw new UnsupportedOperationException();
    }

    public void removeOutgoingEdges(T node) {
      throw new UnsupportedOperationException();
    }
  }

  private static class FilteredNodes implements NumberedNodeManager {
    private final NumberedNodeManager nodes;

    private final Set subset;

    FilteredNodes(NumberedNodeManager nodes, Set subset) {
      this.nodes = nodes;
      this.subset = subset;
    }

    public int getNumber(T N) {
      if (subset.contains(N))
        return nodes.getNumber(N);
      else
        return -1;
    }

    public T getNode(int number) {
      T N = nodes.getNode(number);
      if (subset.contains(N))
        return N;
      else
        throw new NoSuchElementException();
    }

    public int getMaxNumber() {
      int max = -1;
      for (Iterator NS = nodes.iterator(); NS.hasNext();) {
        T N = NS.next();
        if (subset.contains(N) && getNumber(N) > max) {
          max = getNumber(N);
        }
      }

      return max;
    }

    private Iterator filterNodes(Iterator nodeIterator) {
      return new FilterIterator(nodeIterator, new Filter() {
        public boolean accepts(Object o) {
          return subset.contains(o);
        }
      });
    }

    public Iterator iterateNodes(IntSet s) {
      return filterNodes(nodes.iterateNodes(s));
    }

    public Iterator iterator() {
      return filterNodes(nodes.iterator());
    }

    public int getNumberOfNodes() {
      return subset.size();
    }

    public void addNode(T n) {
      throw new UnsupportedOperationException();
    }

    public void removeNode(T n) {
      throw new UnsupportedOperationException();
    }

    public boolean containsNode(T N) {
      return subset.contains(N);
    }
  }

  private final ControlFlowGraph cfg;

  private final FilteredNodes nodes;

  private final FilteredCFGEdges edges;

  private PrunedCFG(final ControlFlowGraph cfg, final EdgeFilter filter) {
    this.cfg = cfg;
    Graph temp = new AbstractNumberedGraph() {
      private final NumberedEdgeManager edges = new FilteredCFGEdges(cfg, cfg, filter);

      @Override
      protected NumberedNodeManager getNodeManager() {
        return cfg;
      }

      @Override
      protected NumberedEdgeManager getEdgeManager() {
        return edges;
      }
    };

    Set reachable = DFS.getReachableNodes(temp, Collections.singleton(cfg.entry()));
    Set back = DFS.getReachableNodes(GraphInverter.invert(temp), Collections.singleton(cfg.exit()));
    reachable.retainAll(back);
/** BEGIN Custom change: entry and exit in every cfg */
    reachable.add(cfg.entry());
    reachable.add(cfg.exit());
/** END Custom change: entry and exit in every cfg */
        
    this.nodes = new FilteredNodes(cfg, reachable);
    this.edges = new FilteredCFGEdges(cfg, nodes, filter);
  }

  @Override
  protected NumberedNodeManager getNodeManager() {
    return nodes;
  }

  @Override
  protected NumberedEdgeManager getEdgeManager() {
    return edges;
  }

  public List getExceptionalSuccessors(final T N) {
    ArrayList result = new ArrayList();
    for (Iterator it = edges.getExceptionalSuccessors(N); it.hasNext();) {
      result.add(it.next());
    }
    return result;
  }

  public Collection getNormalSuccessors(final T N) {
    return Iterator2Collection.toSet(edges.getNormalSuccessors(N));
  }

  public Collection getExceptionalPredecessors(final T N) {
    return Iterator2Collection.toSet(edges.getExceptionalPredecessors(N));
  }

  public Collection getNormalPredecessors(final T N) {
    return Iterator2Collection.toSet(edges.getNormalPredecessors(N));
  }

  public T entry() {
    return cfg.entry();
  }

  public T exit() {
    return cfg.exit();
  }

  public T getBlockForInstruction(int index) {
    return cfg.getBlockForInstruction(index);
  }

  public I[] getInstructions() {
    return cfg.getInstructions();
  }

  public int getProgramCounter(int index) {
    return cfg.getProgramCounter(index);
  }

  public IMethod getMethod() {
    return cfg.getMethod();
  }

  public BitVector getCatchBlocks() {
    BitVector result = new BitVector();
    BitVector blocks = cfg.getCatchBlocks();
    int i = 0;
    while ((i = blocks.nextSetBit(i)) != -1) {
      if (nodes.containsNode(getNode(i))) {
        result.set(i);
      }
    }

    return result;
  }

  public IntSet getPhiIndices(T bb) {
    assert containsNode(bb);
    assert cfg.containsNode(bb);

    int i = 0;
    MutableIntSet valid = IntSetUtil.make();
    for (Iterator pbs = cfg.getPredNodes(bb); pbs.hasNext(); i++) {
      if (nodes.containsNode(pbs.next())) {
        valid.add(i);
      }
    }

    return valid;
  }

}
File
PrunedCFG.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.cha;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.ClassLoaderFactory;
import com.ibm.wala.classLoader.ClassLoaderFactoryImpl;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IClassLoader;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.ShrikeClass;
import com.ibm.wala.ipa.callgraph.AnalysisScope;

import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.MapIterator;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.functions.Function;
import com.ibm.wala.util.ref.CacheReference;
import com.ibm.wala.util.ref.ReferenceCleanser;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * Simple implementation of a class hierarchy.
 * 
 * Note that this class hierarchy implementation is mutable. You can add classes via addClass(). You can add a class even if
 * c.getClassLoader() does not appear in getLoaders().
 */
public class ClassHierarchy implements IClassHierarchy {

  private static final boolean DEBUG = false;

  /**
   * Languages that contribute classes to the set represented in this hierarchy. The languages may for example be related by
   * inheritance (e.g. X10 derives from Java, and shares a common type hierarchy rooted at java.lang.Object).
   */
  private final Set languages = HashSetFactory.make();

  /**
   * For each {@link IClass} c in this class hierarchy, this map maps c.getReference() to the {@link Node}
   * 
   * Note that this class provides an iterator() over this map, and that some WALA utilities (e.g. ReferenceCleanser) must iterate
   * over all classes. But also note that the class hierarchy is mutable (addClass()). So, when trying to run multiple threads, we
   * could see a race condition between iterator() and addClass(). With a normal {@link HashMap}, this would result in a
   * {@link ConcurrentModificationException}. But with a {@link ConcurrentHashMap}, at least the code merrily chugs along,
   * tolerating the race.
   */
  final private Map map = new ConcurrentHashMap();

  /**
   * {@link TypeReference} for the root type
   */
  private TypeReference rootTypeRef;

  /**
   * root node of the class hierarchy
   */
  private Node root;

  /**
   * An object which defines class loaders.
   */
  final private ClassLoaderFactory factory;

  /**
   * The loaders used to define this class hierarchy.
   */
  final private IClassLoader[] loaders;

  /**
   * A mapping from IClass -> Selector -> Set of IMethod
   */
  final private HashMap targetCache = HashMapFactory.make();

  /**
   * Governing analysis scope
   */
  private final AnalysisScope scope;

  /**
   * A mapping from IClass (representing an interface) -> Set of IClass that implement that interface
   */
  private final Map> implementors = HashMapFactory.make();

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection subclassesOfError;

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection subTypeRefsOfError;

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection runtimeExceptionClasses;

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection runtimeExceptionTypeRefs;

  /**
   * Return a set of {@link IClass} that holds all superclasses of klass
   * 
   * @param klass class in question
   * @return Set the result set
   */
  private Set computeSuperclasses(IClass klass) {
    if (DEBUG) {
      System.err.println("computeSuperclasses: " + klass);
    }

    Set result = HashSetFactory.make(3);

    klass = klass.getSuperclass();

    while (klass != null) {
      if (DEBUG) {
        System.err.println("got superclass " + klass);
      }
      result.add(klass);
      klass = klass.getSuperclass();
      if (klass != null && klass.getReference().getName().equals(rootTypeRef.getName())) {
        if (!klass.getReference().getClassLoader().equals(rootTypeRef.getClassLoader())) {
          throw new IllegalStateException("class " + klass + " is invalid, unexpected classloader");
        }
      }
    }
    return result;
  }

  private ClassHierarchy(AnalysisScope scope, ClassLoaderFactory factory, Language language, IProgressMonitor progressMonitor)
      throws ClassHierarchyException, IllegalArgumentException {
    this(scope, factory, Collections.singleton(language), progressMonitor);
  }

  private ClassHierarchy(AnalysisScope scope, ClassLoaderFactory factory, IProgressMonitor progressMonitor)
      throws ClassHierarchyException, IllegalArgumentException {
    this(scope, factory, scope.getLanguages(), progressMonitor);
  }

  private ClassHierarchy(AnalysisScope scope, ClassLoaderFactory factory, Collection languages,
      IProgressMonitor progressMonitor) throws ClassHierarchyException, IllegalArgumentException {
    // now is a good time to clear the warnings globally.
    // TODO: think of a better way to guard against warning leaks.
    Warnings.clear();

    if (factory == null) {
      throw new IllegalArgumentException();
    }
    if (scope.getLanguages().size() == 0) {
      throw new IllegalArgumentException("AnalysisScope must contain at least 1 language");
    }
    this.scope = scope;
    this.factory = factory;
    Set langNames = HashSetFactory.make();
    for (Language lang : languages) {
      this.languages.add(lang);
      this.languages.addAll(lang.getDerivedLanguages());
      langNames.add(lang.getName());
    }
    for (Language lang : this.languages) {
      if (lang.getRootType() != null && lang.getRootType() != this.rootTypeRef) {
        if (this.rootTypeRef != null) {
          throw new IllegalArgumentException("AnalysisScope must have only 1 root type: " + lang.getRootType() + ", " + rootTypeRef);
        } else {
          this.rootTypeRef = lang.getRootType();
        }
      }
    }
    try {
      int numLoaders = 0;
      for (ClassLoaderReference ref : scope.getLoaders()) {
        if (langNames.contains(ref.getLanguage())) {
          numLoaders++;
        }
      }

      loaders = new IClassLoader[numLoaders];
      int idx = 0;

      if (progressMonitor != null) {
        progressMonitor.beginTask("Build Class Hierarchy", numLoaders);
      }
      for (ClassLoaderReference ref : scope.getLoaders()) {
        if (progressMonitor != null) {
          if (progressMonitor.isCanceled()) {
            throw new CancelCHAConstructionException();
          }
        }

        if (langNames.contains(ref.getLanguage())) {
          IClassLoader icl = factory.getLoader(ref, this, scope);
          loaders[idx++] = icl;

          if (progressMonitor != null) {
            progressMonitor.worked(1);
          }
        }
      }

      for (IClassLoader icl : loaders) {
        addAllClasses(icl, progressMonitor);

        if (progressMonitor != null) {
          progressMonitor.worked(1);
        }
      }

    } catch (IOException e) {
      throw new ClassHierarchyException("factory.getLoader failed " + e);
    } finally {
      if (progressMonitor != null) {
        progressMonitor.done(); // In case an exception is thrown.
      }
    }

    if (root == null) {
      throw new ClassHierarchyException("failed to load root " + rootTypeRef + " of class hierarchy");
    }

    // perform numbering for subclass tests.
    numberTree();
    ReferenceCleanser.registerClassHierarchy(this);
  }

  /**
   * Add all classes in a class loader to the hierarchy.
   */
  private void addAllClasses(IClassLoader loader, IProgressMonitor progressMonitor) throws CancelCHAConstructionException {
    if (DEBUG) {
      System.err.println(("Add all classes from loader " + loader));
    }
    Collection toRemove = HashSetFactory.make();
    for (Iterator it = loader.iterateAllClasses(); it.hasNext();) {
      if (progressMonitor != null) {
        if (progressMonitor.isCanceled()) {
          throw new CancelCHAConstructionException();
        }
      }
      IClass klass = it.next();
      boolean added = addClass(klass);
      if (!added) {
        toRemove.add(klass);
      }
    }
    loader.removeAll(toRemove);

  }

  /**
   * @return true if the add succeeded; false if it failed for some reason
   * @throws IllegalArgumentException if klass is null
   */
  public boolean addClass(IClass klass) {
    if (klass == null) {
      throw new IllegalArgumentException("klass is null");
    }
    if (klass.getReference().getName().equals(rootTypeRef.getName())) {
      if (!klass.getReference().getClassLoader().equals(rootTypeRef.getClassLoader())) {
        throw new IllegalArgumentException("class " + klass + " is invalid, unexpected classloader");
      }
    }
    if (DEBUG) {
      System.err.println(("Attempt to add class " + klass));
    }
    Set loadedSuperclasses;
    Collection loadedSuperInterfaces;
    try {
      loadedSuperclasses = computeSuperclasses(klass);
      loadedSuperInterfaces = klass.getAllImplementedInterfaces();
    } catch (Exception e) {
      // a little cleanup
      if (klass instanceof ShrikeClass) {
        if (DEBUG) {
          System.err.println(("Exception.  Clearing " + klass));
        }
      }
      Warnings.add(ClassExclusion.create(klass.getReference(), e.getMessage()));
      return false;
    }
    Node node = findOrCreateNode(klass);

    if (klass.getReference().equals(this.rootTypeRef)) {
      // there is only one root
      assert root == null;
      root = node;
    }

    Set workingSuperclasses = HashSetFactory.make(loadedSuperclasses);
    while (node != null) {
      IClass c = node.getJavaClass();
      IClass superclass = null;
      superclass = c.getSuperclass();
      if (superclass != null) {
        workingSuperclasses.remove(superclass);
      } else {
        Node supernode = findOrCreateNode(superclass);
        if (DEBUG) {
          System.err.println(("addChild " + node.getJavaClass() + " to " + supernode.getJavaClass()));
        }
        supernode.addChild(node);
        if (supernode.getJavaClass().getReference().equals(rootTypeRef)) {
          node = null;
        } else {
          node = supernode;
        }
      } else {
        node = null;
      }
    }

    if (loadedSuperInterfaces != null) {
      for (Iterator it3 = loadedSuperInterfaces.iterator(); it3.hasNext();) {
        IClass iface = (IClass) it3.next();
        try {
          // make sure we'll be able to load the interface!
          computeSuperclasses(iface);
        } catch (IllegalStateException e) {
          Warnings.add(ClassExclusion.create(iface.getReference(), e.getMessage()));
          continue;
        }
        if (!iface.isInterface()) {
          assert false : "not an interface: " + iface;
        }
        recordImplements(klass, iface);
      }
    }
    return true;
  }

  /**
   * Record that a klass implements a particular interface
   */
  private void recordImplements(IClass klass, IClass iface) {
    Set impls = MapUtil.findOrCreateSet(implementors, iface);
    impls.add(klass);
  }

  /**
   * 
   * Find the possible targets of a call to a method reference. Note that if the reference is to an instance initialization method,
   * we assume the method was called with invokespecial rather than invokevirtual.
   * 
   * @param ref method reference
   * @return the set of IMethods that this call can resolve to.
   * @throws IllegalArgumentException if ref is null
   */
  public Collection getPossibleTargets(MethodReference ref) {
    if (ref == null) {
      throw new IllegalArgumentException("ref is null");
    }
    IClassLoader loader;
    try {
      loader = factory.getLoader(ref.getDeclaringClass().getClassLoader(), this, scope);
    } catch (IOException e) {
      throw new UnimplementedError("factory.getLoader failed " + e);
    }
    IClass declaredClass;
    declaredClass = loader.lookupClass(ref.getDeclaringClass().getName());
    if (declaredClass == null) {
      return Collections.emptySet();
    }
    Set targets = HashSetFactory.make();
    targets.addAll(findOrCreateTargetSet(declaredClass, ref));
    return (targets);
  }

  /**
   * Find the possible targets of a call to a method reference
   * 
   * @param ref method reference
   * @return the set of IMethods that this call can resolve to.
   */
  @SuppressWarnings("unchecked")
  private Set findOrCreateTargetSet(IClass declaredClass, MethodReference ref) {
    Map> classCache = (Map>) CacheReference.get(targetCache
        .get(declaredClass));
    if (classCache == null) {
      classCache = HashMapFactory.make(3);
      targetCache.put(declaredClass, CacheReference.make(classCache));
    }
    Set result = classCache.get(ref);
    if (result == null) {
      result = getPossibleTargets(declaredClass, ref);
      classCache.put(ref, result);
    }
    return result;
  }

  /**
   * Find the possible receivers of a call to a method reference
   * 
   * @param ref method reference
   * @return the set of IMethods that this call can resolve to.
   */
  public Set getPossibleTargets(IClass declaredClass, MethodReference ref) {
    if (ref.getName().equals(MethodReference.initAtom)) {
      // for an object init method, use the method alone as a possible target,
      // rather than inspecting subclasses
      IMethod resolvedMethod = resolveMethod(ref);
      assert resolvedMethod != null;
      return Collections.singleton(resolvedMethod);
    }
    if (declaredClass.isInterface()) {
      HashSet result = HashSetFactory.make(3);
      Set impls = implementors.get(declaredClass);
      if (impls == null) {
        // give up and return no receivers
        return Collections.emptySet();
      }
      for (Iterator it = impls.iterator(); it.hasNext();) {
        IClass klass = (IClass) it.next();
        if (!klass.isInterface() && !klass.isAbstract()) {
          result.addAll(computeTargetsNotInterface(ref, klass));
        }
      }
      return result;
    } else {
      return computeTargetsNotInterface(ref, declaredClass);
    }

  }

  /**
   * Get the targets for a method ref invoked on a class klass. The klass had better not be an interface.
   * 
   * @param ref method to invoke
   * @param klass declaringClass of receiver
   * @return Set the set of method implementations that might receive the message
   */
  private Set computeTargetsNotInterface(MethodReference ref, IClass klass) {

    Node n = findNode(klass);
    HashSet result = HashSetFactory.make(3);
   * @param clazz class in question
    // if n is null, then for some reason this class is excluded
    // from the analysis. Return a result of no targets.
    if (n == null)
      return result;

    Selector selector = ref.getSelector();

    // try to resolve the method by walking UP the class hierarchy
    IMethod resolved = resolveMethod(klass, selector);

    if (resolved != null) {
      result.add(resolved);
    }

    // find any receivers that override the method with inheritance
    result.addAll(computeOverriders(n, selector));

    return result;
  }

  /**
   * Return the unique receiver of an invocation of method on an object of type m.getDeclaredClass
   * 
   * @param m
   * @return IMethod, or null if no appropriate receiver is found.
   * @throws IllegalArgumentException if m is null
   */
  public IMethod resolveMethod(MethodReference m) {
    if (m == null) {
      throw new IllegalArgumentException("m is null");
    }
    IClass receiver = lookupClass(m.getDeclaringClass());
    if (receiver == null) {
      return null;
    }
    Selector selector = m.getSelector();
    return resolveMethod(receiver, selector);
  }

  /**
   * @return the canonical IField that represents a given field , or null if none found
   * @throws IllegalArgumentException if f is null
   */
  public IField resolveField(FieldReference f) {
    if (f == null) {
      throw new IllegalArgumentException("f is null");
    }
    IClass klass = lookupClass(f.getDeclaringClass());
    if (klass == null) {
      return null;
    }
    return resolveField(klass, f);
  }

  /**
   * @return the canonical IField that represents a given field , or null if none found
   * @throws IllegalArgumentException if f is null
   * @throws IllegalArgumentException if klass is null
   */
  public IField resolveField(IClass klass, FieldReference f) {
    if (klass == null) {
      } catch (ClassHierarchyException e1) {
      throw new IllegalArgumentException("klass is null");
    }
    if (f == null) {
      throw new IllegalArgumentException("f is null");
    }
    return klass.getField(f.getName(), f.getFieldType().getName());
  }

  /**
   * Return the unique target of an invocation of method on an object of type declaringClass
   * 
   * @param receiverClass type of receiver
   * @param selector method signature
   * @return Method resolved method abstraction
   * @throws IllegalArgumentException if receiverClass is null
   */
  public IMethod resolveMethod(IClass receiverClass, Selector selector) {
    if (receiverClass == null) {
      throw new IllegalArgumentException("receiverClass is null");
    }
    IMethod result = findMethod(receiverClass, selector);
    if (result != null) {
      return result;
    } else {
      IClass superclass = null;
      superclass = receiverClass.getSuperclass();
      if (superclass == null) {
        if (DEBUG) {
          System.err.println(("resolveMethod(" + selector + ") failed: method not found"));
        }
        return null;
      } else {
        if (DEBUG) {
          System.err.println(("Attempt to resolve for " + receiverClass + " in superclass: " + superclass + " " + selector));
        }
        return resolveMethod(superclass, selector);
      }
    }
  }

  /**
   * Does a particular class contain (implement) a particular method?
   * @param selector method selector
   * @return the method if found, else null
   */
  private IMethod findMethod(IClass clazz, Selector selector) {
    return clazz.getMethod(selector);
  }

  /**
   * Get the set of subclasses of a class that provide implementations of a method
   * 
   * @param node abstraction of class in question
   * @param selector method signature
   * @return Set set of IMethods that override the method
   */
  private Set computeOverriders(Node node, Selector selector) {
    HashSet result = HashSetFactory.make(3);
    for (Iterator it = node.getChildren(); it.hasNext();) {

      Node child = it.next();
      IMethod m = findMethod(child.getJavaClass(), selector);
      if (m != null) {
        result.add(m);
      }
      result.addAll(computeOverriders(child, selector));
    }
    return result;
  }

  private Node findNode(IClass klass) {
    return map.get(klass.getReference());
  }

  private Node findOrCreateNode(IClass klass) {
    Node result = map.get(klass.getReference());
    if (result == null) {
      result = new Node(klass);
      map.put(klass.getReference(), result);
    }
    return result;
  }

  @Override
  public String toString() {
    StringBuffer result = new StringBuffer(100);
    recursiveStringify(root, result);
    return result.toString();
  }

  private void recursiveStringify(Node n, StringBuffer buffer) {
    buffer.append(n.toString()).append("\n");
    for (Iterator it = n.getChildren(); it.hasNext();) {
      Node child = it.next();
      recursiveStringify(child, buffer);
    }
  }

  /**
   * Number the class hierarchy tree to support efficient subclass tests. After numbering the tree, n1 is a child of n2 iff n2.left
   * <= n1.left ^ n1.left <= n2.right. Described as "relative numbering" by Vitek, Horspool, and Krall, OOPSLA 97
   * 
   * TODO: this implementation is recursive; un-recursify if needed
   */
  private int nextNumber = 1;

  private void numberTree() {
    assert root != null;
    visitForNumbering(root);
  }

  private void visitForNumbering(Node N) {
    N.left = nextNumber++;
    for (Iterator it = N.children.iterator(); it.hasNext();) {
      Node C = it.next();
      visitForNumbering(C);
    }
    N.right = nextNumber++;
  }

  /**
   * internal representation of a node in the class hiearachy, representing one java class.
   */
  private static final class Node {

    private final IClass klass;

    final private Set children = HashSetFactory.make(3);

    // the following two fields are used for efficient subclass tests.
    // After numbering the tree, n1 is a child of n2 iff
    // n2.left <= n1.left ^ n1.left <= n2.right.
    // Described as "relative numbering" by Vitek, Horspool, and Krall, OOPSLA
    // 97
    private int left = -1;

    private int right = -1;

    Node(IClass klass) {
      this.klass = klass;
    }

    boolean isInterface() {
      return klass.isInterface();
    }

    IClass getJavaClass() {
      return klass;
    }

    void addChild(Node child) {
      children.add(child);
    }

    Iterator getChildren() {
      return children.iterator();
    }

    @Override
    public String toString() {
      StringBuffer result = new StringBuffer(100);
      result.append(klass.toString()).append(":");
      for (Iterator i = children.iterator(); i.hasNext();) {
        Node n = i.next();
        result.append(n.klass.toString());
        if (i.hasNext())
          result.append(",");
      }
      return result.toString();
    }

    @Override
    public int hashCode() {
      return klass.hashCode() * 929;
    }

    @Override
    public boolean equals(Object obj) {
      return this == obj;
    }

  }

  public ClassLoaderFactory getFactory() {
    return factory;
  }

  /**
   * @throws IllegalArgumentException if A is null
   */
  public IClass getLeastCommonSuperclass(IClass a, IClass b) {
    assert (a.getClassLoader().getLanguage().equals(b.getClassLoader().getLanguage()));
    Language lang = a.getClassLoader().getLanguage();
    if (a == null) {
      throw new IllegalArgumentException("A is null");
    }
    TypeReference tempA = a.getReference();
    if (a.equals(b)) {
      return a;
    } else if (tempA.equals(TypeReference.Null)) {
      return b;
    } else if (b.getReference().equals(TypeReference.Null)) {
      return a;
    } else if (b.getReference().equals(lang.getRootType())) {
      return b;
    } else {
      Node n = map.get(b.getReference());
      if (n == null) {
        assert n != null : "null n for " + b;
      }
      Set superB;
      try {
        superB = getSuperclasses(b);
        e1.printStackTrace();
        Assertions.UNREACHABLE();
        superB = null;
      }
      IClass aa = a;
      while (aa != null) {
        if (superB.contains(aa)) {
          return aa;
        }
        aa = aa.getSuperclass();
      }
      Set superA;
      try {
        superA = getSuperclasses(a);
      } catch (ClassHierarchyException e1) {
        e1.printStackTrace();
        Assertions.UNREACHABLE();
        superA = null;
      }
      Assertions.UNREACHABLE("getLeastCommonSuperclass " + tempA + " " + b + ": " + superA + ", " + superB);
      return null;
    }
  }

  private Set getSuperclasses(IClass c) throws ClassHierarchyException {
    HashSet result = HashSetFactory.make(3);
    while (c.getSuperclass() != null) {
      result.add(c.getSuperclass());
      c = c.getSuperclass();
    }
    return result;
  }

  /*
   * @see com.ibm.wala.ipa.cha.IClassHierarchy#getLeastCommonSuperclass(com.ibm.wala.types.TypeReference,
   * com.ibm.wala.types.TypeReference)
   */
  public TypeReference getLeastCommonSuperclass(TypeReference a, TypeReference b) {
    if (a == null) {
      throw new IllegalArgumentException("a is null");
    }
    if (a.equals(b))
      return a;
    IClass aClass = lookupClass(a);
    IClass bClass = lookupClass(b);
    if (aClass == null || bClass == null) {
      // One of the classes is not in scope. Give up.
      if (aClass != null) {
        return aClass.getClassLoader().getLanguage().getRootType();
      } else if (bClass != null) {
        return bClass.getClassLoader().getLanguage().getRootType();
      } else {
        return getRootClass().getReference();
      }
    }
    return getLeastCommonSuperclass(aClass, bClass).getReference();
  }

  /**
   * Find a class in this class hierarchy.
   * 
   * @return the {@link IClass} for a if found; null if can't find the class.
   * @throws IllegalArgumentException if A is null
   */
  public IClass lookupClass(TypeReference a) {
    if (a == null) {
      throw new IllegalArgumentException("a is null");
    }
    ClassLoaderReference loader = a.getClassLoader();

    ClassLoaderReference parent = loader.getParent();
    // first delegate lookup to the parent loader.
    if (parent != null) {
      TypeReference p = TypeReference.findOrCreate(parent, a.getName());
      IClass c = lookupClass(p);
      if (c != null) {
        return c;
      }
    }

    // lookup in the parent failed. lookup based on the declared loader of a.
    if (a.isArrayType()) {
      TypeReference elt = a.getInnermostElementType();
      if (elt.isPrimitiveType()) {
        // look it up with the primordial loader.
        return getRootClass().getClassLoader().lookupClass(a.getName());
      } else {
        IClass c = lookupClass(elt);
        if (c == null) {
          // can't load the element class, so give up.
/** BEGIN Custom change: remember unresolved classes */
          unresolved.add(elt);
/** END Custom change: remember unresolved classes */
          return null;
        } else {
          // we know it comes from c's class loader.
          return c.getClassLoader().lookupClass(a.getName());
        }
      }
    } else {
      Node n = map.get(a);
      if (n != null) {
        return n.klass;
/** BEGIN Custom change: remember unresolved classes */
        unresolved.add(a);
/** END Custom change: remember unresolved classes */
        return null;
      }
    }
  }

  private boolean slowIsSubclass(IClass sub, IClass sup) {
    if (sub == sup) {
      return true;
    } else {
      IClass parent = sub.getSuperclass();
      if (parent == null) {
        return false;
      } else {
        return slowIsSubclass(parent, sup);
      }
    }
  }

  /**
   * Is c a subclass of T?
   * 
   * @throws IllegalArgumentException if c is null
   */
  public boolean isSubclassOf(IClass c, IClass t) {
    if (c == null) {
      throw new IllegalArgumentException("c is null");
    }
    assert t != null : "null T";

    if (c.isArrayClass()) {
      if (t.getReference() == TypeReference.JavaLangObject) {
        return true;
      } else if (t.getReference().isArrayType()) {
        TypeReference elementType = t.getReference().getArrayElementType();
        if (elementType.isPrimitiveType()) {
          return elementType.equals(c.getReference().getArrayElementType());
        } else {
          IClass elementKlass = lookupClass(elementType);
          if (elementKlass == null) {
            // uh oh.
            Warnings.add(ClassHierarchyWarning.create("could not find " + elementType));
            return false;
          }
          IClass ce = ((ArrayClass) c).getElementClass();
          if (ce == null) {
            return false;
          }
          if (elementKlass.isInterface()) {
            return implementsInterface(ce, elementKlass);
          } else {
            return isSubclassOf(ce, elementKlass);
          }
        }
      } else {
        return false;
      }
    } else {
      if (t.getReference().isArrayType()) {
        return false;
      }
      if (c.getReference().equals(t.getReference())) {
        return true;
      }
      Node n1 = map.get(c.getReference());
      if (n1 == null) {
        // some wacky case, like a FakeRootClass
        return false;
      }
      Node n2 = map.get(t.getReference());
      if (n2 == null) {
        // some wacky case, like a FakeRootClass
        return false;
      }
      if (n1.left == -1) {
        return slowIsSubclass(c, t);
      } else if (n2.left == -1) {
        return slowIsSubclass(c, t);
      } else {
        return (n2.left <= n1.left) && (n1.left <= n2.right);
      }
    }
  }

  /**
   * Does c implement i?
   * 
   * @return true iff i is an interface and c is a class that implements i, or c is an interface that extends i.
   */
  public boolean implementsInterface(IClass c, IClass i) {
    if (i == null) {
      throw new IllegalArgumentException("Cannot ask implementsInterface with i == null");
    }
    if (c == null) {
      throw new IllegalArgumentException("Cannot ask implementsInterface with c == null");
    }
    if (!i.isInterface()) {
      return false;
    }
    if (c.equals(i)) {
      return true;
    }
    if (c.isArrayClass()) {
      // arrays implement Cloneable and Serializable
   */
      return i.equals(lookupClass(TypeReference.JavaLangCloneable)) || i.equals(lookupClass(TypeReference.JavaIoSerializable));
    }
    Set impls = implementors.get(i);
    if (impls != null && impls.contains(c)) {
      return true;
    }
    return false;
  }

  /**
   * Return set of all subclasses of type in the Class Hierarchy TODO: Tune this implementation. Consider caching if necessary.
   */
  public Collection computeSubClasses(TypeReference type) {
    IClass t = lookupClass(type);
    if (t == null) {
      throw new IllegalArgumentException("could not find class for TypeReference " + type);
    }
    // a hack: TODO: work on better caching
    if (t.getReference().equals(TypeReference.JavaLangError)) {
      if (subclassesOfError == null) {
        subclassesOfError = computeSubClassesInternal(t);
      }
      return subclassesOfError;
    } else if (t.getReference().equals(TypeReference.JavaLangRuntimeException)) {
      if (runtimeExceptionClasses == null) {
        runtimeExceptionClasses = computeSubClassesInternal(t);
      }
      return runtimeExceptionClasses;
    } else {
      return computeSubClassesInternal(t);
    }
  }

  /**
   * Solely for optimization; return a Collection representing the subclasses of Error
   * 
   * kind of ugly. a better scheme?
   */
  public Collection getJavaLangErrorTypes() {
    if (subTypeRefsOfError == null) {
      computeSubClasses(TypeReference.JavaLangError);
      subTypeRefsOfError = HashSetFactory.make(subclassesOfError.size());
      for (Iterator it = subclassesOfError.iterator(); it.hasNext();) {
        IClass klass = (IClass) it.next();
        subTypeRefsOfError.add(klass.getReference());
      }
    }
    return Collections.unmodifiableCollection(subTypeRefsOfError);
  }

  /**
   * Solely for optimization; return a Collection representing the subclasses of RuntimeException
   * 
   * kind of ugly. a better scheme?
   */
  public Collection getJavaLangRuntimeExceptionTypes() {
    if (runtimeExceptionTypeRefs == null) {
      computeSubClasses(TypeReference.JavaLangRuntimeException);
      runtimeExceptionTypeRefs = HashSetFactory.make(runtimeExceptionClasses.size());
      for (Iterator it = runtimeExceptionClasses.iterator(); it.hasNext();) {
        IClass klass = (IClass) it.next();
        runtimeExceptionTypeRefs.add(klass.getReference());
      }
    }
    return Collections.unmodifiableCollection(runtimeExceptionTypeRefs);
  }

  /**
   * Return set of all subclasses of type in the Class Hierarchy TODO: Tune this implementation. Consider caching if necessary.
   * 
   * @return Set of IClasses
   */
  private Set computeSubClassesInternal(IClass T) {
    if (T.isArrayClass()) {
      return Collections.singleton(T);
    }
    Node node = findNode(T);
    assert node != null : "null node for class " + T;
    HashSet result = HashSetFactory.make(3);
    result.add(T);
    for (Iterator it = node.getChildren(); it.hasNext();) {
      Node child = it.next();
      result.addAll(computeSubClasses(child.klass.getReference()));
    }
    return result;
  }

  public boolean isInterface(TypeReference type) {
    IClass T = lookupClass(type);
    assert T != null : "Null lookup for " + type;
    return T.isInterface();
  }

  /**
   * TODO: tune this if necessary
   * 
   * @param type an interface
   * @return Set of IClass that represent implementors of the interface
  public Set getImplementors(TypeReference type) {
    IClass T = lookupClass(type);
    Set result = implementors.get(T);
    if (result == null) {
      return Collections.emptySet();
    }
    return Collections.unmodifiableSet(result);
  }

  public Iterator iterator() {
    Function toClass = new Function() {
      public IClass apply(Node n) {
        return n.klass;
      }
    };
    return new MapIterator(map.values().iterator(), toClass);
  }

  /**
   * @return The number of classes present in the class hierarchy.
   */
  public int getNumberOfClasses() {
    return map.keySet().size();
  }

  public IClassLoader[] getLoaders() {
    return loaders;
  }

  public IClassLoader getLoader(ClassLoaderReference loaderRef) {
    for (int i = 0; i < loaders.length; i++) {
      if (loaders[i].getReference().equals(loaderRef)) {
        return loaders[i];
      }
    }
    Assertions.UNREACHABLE();
    return null;
  }

  public AnalysisScope getScope() {
    return scope;
  }

  /**
  }

   * @return the number of classes that immediately extend klass. if klass is an array class A[][]...[], we return number of
   *         immediate subclasses of A. If A is primitive, we return 0.
   */
  public int getNumberOfImmediateSubclasses(IClass klass) {
    if (klass.isArrayClass()) {
      IClass innermost = getInnermostTypeOfArrayClass(klass);
      return innermost == null ? 0 : getNumberOfImmediateSubclasses(innermost);
    }
    Node node = findNode(klass);
    return node.children.size();
  }

  /**
   * @param klass
   * @return the classes that immediately extend klass. if klass is an array class A[][]...[], we return array classes B[][]...[]
   *         (same dimensionality) where B is an immediate subclass of A. If A is primitive, we return the empty set.
   */
  public Collection getImmediateSubclasses(IClass klass) {
    if (klass.isArrayClass()) {
      return getImmediateArraySubclasses(klass);
    }
    Function node2Class = new Function() {
      public IClass apply(Node n) {
        return n.klass;
      }
    };
    return Iterator2Collection.toSet(new MapIterator(findNode(klass).children.iterator(), node2Class));
  }

  private Collection getImmediateArraySubclasses(IClass klass) {
    IClass innermost = getInnermostTypeOfArrayClass(klass);
    if (innermost == null) {
      return Collections.emptySet();
    }
    Collection innermostSubclasses = getImmediateSubclasses(innermost);
    int dim = klass.getReference().getDimensionality();
    Collection result = HashSetFactory.make();
    for (IClass k : innermostSubclasses) {
      TypeReference ref = k.getReference();
      for (int i = 0; i < dim; i++) {
        ref = ref.getArrayTypeForElementType();
      }
      result.add(lookupClass(ref));
    }
    return result;
  }

  /**
   * for an array class, get the innermost type, or null if it's primitive
   */
  private IClass getInnermostTypeOfArrayClass(IClass klass) {
    TypeReference result = klass.getReference();
    while (result.isArrayType()) {
      result = result.getArrayElementType();
    }
    return result.isPrimitiveType() ? null : lookupClass(result);
  }

  /**
   * @return a ClassHierarchy object representing the analysis scope
   * @throws ClassHierarchyException
   */
  public static ClassHierarchy make(AnalysisScope scope) throws ClassHierarchyException {
    if (scope == null) {
      throw new IllegalArgumentException("null scope");
    }
    return make(scope, new ClassLoaderFactoryImpl(scope.getExclusions()));
  }

  /**
   * temporarily marking this internal to avoid infinite sleep with randomly chosen IProgressMonitor.
   */
  public static ClassHierarchy make(AnalysisScope scope, IProgressMonitor monitor) throws ClassHierarchyException {
    if (scope == null) {
      throw new IllegalArgumentException("null scope");
    }
    return make(scope, new ClassLoaderFactoryImpl(scope.getExclusions()), monitor);
  }

  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory) throws ClassHierarchyException {
    if (scope == null) {
      throw new IllegalArgumentException("null scope");
    }
    if (factory == null) {
      throw new IllegalArgumentException("null factory");
    }
    return new ClassHierarchy(scope, factory, null);
  }

  /**
   * temporarily marking this internal to avoid infinite sleep with randomly chosen IProgressMonitor.
   */
  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, IProgressMonitor monitor)
      throws ClassHierarchyException {
    return new ClassHierarchy(scope, factory, monitor);
  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, Set languages)
      throws ClassHierarchyException {
    return new ClassHierarchy(scope, factory, languages, null);
  }

  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, Language language)
      throws ClassHierarchyException {
    return new ClassHierarchy(scope, factory, language, null);
  }

  /**
   * temporarily marking this internal to avoid infinite sleep with randomly chosen IProgressMonitor. TODO: nanny for testgen
   */
  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, Language language, IProgressMonitor monitor)
      throws ClassHierarchyException {
    if (factory == null) {
      throw new IllegalArgumentException("null factory");
    }
    return new ClassHierarchy(scope, factory, language, monitor);
  }

  public IClass getRootClass() {
    return root.getJavaClass();
  }

  public boolean isRootClass(IClass c) throws IllegalArgumentException {
    if (c == null) {
      throw new IllegalArgumentException("c == null");
    }
    return c.equals(root.getJavaClass());
  }

  public int getNumber(IClass c) {
    return map.get(c.getReference()).left;
  }

  /**
   * A warning for when we fail to resolve the type for a checkcast
   */
  private static class ClassExclusion extends Warning {

    final TypeReference klass;

    final String message;

    ClassExclusion(TypeReference klass, String message) {
      super(Warning.MODERATE);
      this.klass = klass;
      this.message = message;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + klass + " " + message;
    }

    public static ClassExclusion create(TypeReference klass, String message) {
      return new ClassExclusion(klass, message);
    }
  }

  /**
   * Does an expression c1 x := c2 y typecheck?
   * 
   * i.e. is c2 a subtype of c1?
   * 
   * @throws IllegalArgumentException if c1 is null
   * @throws IllegalArgumentException if c2 is null
   */
  public boolean isAssignableFrom(IClass c1, IClass c2) {
    if (c2 == null) {
      throw new IllegalArgumentException("c2 is null");
    }
    if (c1 == null) {
      throw new IllegalArgumentException("c1 is null");
    }
    if (c1.isInterface()) {
      return implementsInterface(c2, c1);
    } else {
      if (c2.isInterface()) {
        return c1.equals(getRootClass());
      } else {
        return isSubclassOf(c2, c1);
      }
    }
  }

/** BEGIN Custom change: remember unresolved classes */
  private final Set unresolved = HashSetFactory.make();

  public final Set getUnresolvedClasses() {
    return unresolved;
  }

/** END Custom change: remember unresolved classes */
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.cha;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.ClassLoaderFactory;
import com.ibm.wala.classLoader.ClassLoaderFactoryImpl;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IClassLoader;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.ShrikeClass;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.MapIterator;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.functions.Function;
import com.ibm.wala.util.ref.CacheReference;
import com.ibm.wala.util.ref.ReferenceCleanser;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * Simple implementation of a class hierarchy.
 * 
 * Note that this class hierarchy implementation is mutable. You can add classes via addClass(). You can add a class even if
 * c.getClassLoader() does not appear in getLoaders().
 */
public class ClassHierarchy implements IClassHierarchy {

  private static final boolean DEBUG = false;

  /**
   * Languages that contribute classes to the set represented in this hierarchy. The languages may for example be related by
   * inheritance (e.g. X10 derives from Java, and shares a common type hierarchy rooted at java.lang.Object).
   */
  private final Set languages = HashSetFactory.make();

  /**
   * For each {@link IClass} c in this class hierarchy, this map maps c.getReference() to the {@link Node}
   * 
   * Note that this class provides an iterator() over this map, and that some WALA utilities (e.g. ReferenceCleanser) must iterate
   * over all classes. But also note that the class hierarchy is mutable (addClass()). So, when trying to run multiple threads, we
   * could see a race condition between iterator() and addClass(). With a normal {@link HashMap}, this would result in a
   * {@link ConcurrentModificationException}. But with a {@link ConcurrentHashMap}, at least the code merrily chugs along,
   * tolerating the race.
   */
  final private Map map = new ConcurrentHashMap();

  /**
   * {@link TypeReference} for the root type
   */
  private TypeReference rootTypeRef;

  /**
   * root node of the class hierarchy
   */
  private Node root;

  /**
   * An object which defines class loaders.
   */
  final private ClassLoaderFactory factory;

  /**
   * The loaders used to define this class hierarchy.
   */
  final private IClassLoader[] loaders;

  /**
   * A mapping from IClass -> Selector -> Set of IMethod
   */
  final private HashMap targetCache = HashMapFactory.make();

  /**
   * Governing analysis scope
   */
  private final AnalysisScope scope;

  /**
   * A mapping from IClass (representing an interface) -> Set of IClass that implement that interface
   */
  private final Map> implementors = HashMapFactory.make();

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection subclassesOfError;

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection subTypeRefsOfError;

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection runtimeExceptionClasses;

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection runtimeExceptionTypeRefs;

  /**
   * Return a set of {@link IClass} that holds all superclasses of klass
   * 
   * @param klass class in question
   * @return Set the result set
   */
  private Set computeSuperclasses(IClass klass) {
    if (DEBUG) {
      System.err.println("computeSuperclasses: " + klass);
    }

    Set result = HashSetFactory.make(3);

    klass = klass.getSuperclass();

    while (klass != null) {
      if (DEBUG) {
        System.err.println("got superclass " + klass);
      }
      boolean added = result.add(klass);
      if (!added) {
        // oops.  we have A is a sub-class of B and B is a sub-class of A.  blow up.
        throw new IllegalStateException("cycle in the extends relation for class " + klass);
      }
      klass = klass.getSuperclass();
      if (klass != null && klass.getReference().getName().equals(rootTypeRef.getName())) {
        if (!klass.getReference().getClassLoader().equals(rootTypeRef.getClassLoader())) {
    numberTree();
          throw new IllegalStateException("class " + klass + " is invalid, unexpected classloader");
        }
      }
    }
    return result;
  }

  private ClassHierarchy(AnalysisScope scope, ClassLoaderFactory factory, Language language, IProgressMonitor progressMonitor)
      throws ClassHierarchyException, IllegalArgumentException {
    this(scope, factory, Collections.singleton(language), progressMonitor);
  }

  private ClassHierarchy(AnalysisScope scope, ClassLoaderFactory factory, IProgressMonitor progressMonitor)
      throws ClassHierarchyException, IllegalArgumentException {
    this(scope, factory, scope.getLanguages(), progressMonitor);
  }

  private ClassHierarchy(AnalysisScope scope, ClassLoaderFactory factory, Collection languages,
      IProgressMonitor progressMonitor) throws ClassHierarchyException, IllegalArgumentException {
    // now is a good time to clear the warnings globally.
    // TODO: think of a better way to guard against warning leaks.
    Warnings.clear();

    if (factory == null) {
      throw new IllegalArgumentException();
    }
    if (scope.getLanguages().size() == 0) {
      throw new IllegalArgumentException("AnalysisScope must contain at least 1 language");
    }
    this.scope = scope;
    this.factory = factory;
    Set langNames = HashSetFactory.make();
    for (Language lang : languages) {
      this.languages.add(lang);
      this.languages.addAll(lang.getDerivedLanguages());
      langNames.add(lang.getName());
    }
   * 
    for (Language lang : this.languages) {
      if (lang.getRootType() != null && lang.getRootType() != this.rootTypeRef) {
        if (this.rootTypeRef != null) {
          throw new IllegalArgumentException("AnalysisScope must have only 1 root type: " + lang.getRootType() + ", " + rootTypeRef);
        } else {
          this.rootTypeRef = lang.getRootType();
        }
      }
    }
    try {
      int numLoaders = 0;
      for (ClassLoaderReference ref : scope.getLoaders()) {
        if (langNames.contains(ref.getLanguage())) {
          numLoaders++;
        }
      }

      loaders = new IClassLoader[numLoaders];
      int idx = 0;

      if (progressMonitor != null) {
        progressMonitor.beginTask("Build Class Hierarchy", numLoaders);
      }
      for (ClassLoaderReference ref : scope.getLoaders()) {
        if (progressMonitor != null) {
          if (progressMonitor.isCanceled()) {
            throw new CancelCHAConstructionException();
          }
        }

        if (langNames.contains(ref.getLanguage())) {
          IClassLoader icl = factory.getLoader(ref, this, scope);
          loaders[idx++] = icl;

          if (progressMonitor != null) {
            progressMonitor.worked(1);
          }
        }
      }

      for (IClassLoader icl : loaders) {
        addAllClasses(icl, progressMonitor);

        if (progressMonitor != null) {
          progressMonitor.worked(1);
        }
      }

    } catch (IOException e) {
      throw new ClassHierarchyException("factory.getLoader failed " + e);
    } finally {
      if (progressMonitor != null) {
        progressMonitor.done(); // In case an exception is thrown.
      }
    }

    if (root == null) {
      throw new ClassHierarchyException("failed to load root " + rootTypeRef + " of class hierarchy");
    }

    // perform numbering for subclass tests.
    ReferenceCleanser.registerClassHierarchy(this);
  }

  /**
   * Add all classes in a class loader to the hierarchy.
   */
  private void addAllClasses(IClassLoader loader, IProgressMonitor progressMonitor) throws CancelCHAConstructionException {
    if (DEBUG) {
      System.err.println(("Add all classes from loader " + loader));
    }
    Collection toRemove = HashSetFactory.make();
    for (Iterator it = loader.iterateAllClasses(); it.hasNext();) {
      if (progressMonitor != null) {
        if (progressMonitor.isCanceled()) {
          throw new CancelCHAConstructionException();
        }
      }
      IClass klass = it.next();
      boolean added = addClass(klass);
      if (!added) {
        toRemove.add(klass);
      }
    }
    loader.removeAll(toRemove);

  }

  /**
   * @return true if the add succeeded; false if it failed for some reason
   * @throws IllegalArgumentException if klass is null
   */
  public boolean addClass(IClass klass) {
    if (klass == null) {
      throw new IllegalArgumentException("klass is null");
    }
    if (klass.getReference().getName().equals(rootTypeRef.getName())) {
      if (!klass.getReference().getClassLoader().equals(rootTypeRef.getClassLoader())) {
        throw new IllegalArgumentException("class " + klass + " is invalid, unexpected classloader");
      }
    }
    if (DEBUG) {
      System.err.println(("Attempt to add class " + klass));
    }
    Set loadedSuperclasses;
    Collection loadedSuperInterfaces;
    try {
      loadedSuperclasses = computeSuperclasses(klass);
      loadedSuperInterfaces = klass.getAllImplementedInterfaces();
    } catch (Exception e) {
      // a little cleanup
      if (klass instanceof ShrikeClass) {
        if (DEBUG) {
          System.err.println(("Exception.  Clearing " + klass));
        }
      }
      Warnings.add(ClassExclusion.create(klass.getReference(), e.getMessage()));
      return false;
    }
    Node node = findOrCreateNode(klass);

    if (klass.getReference().equals(this.rootTypeRef)) {
      // there is only one root
      assert root == null;
      root = node;
    }

    Set workingSuperclasses = HashSetFactory.make(loadedSuperclasses);
    while (node != null) {
      IClass c = node.getJavaClass();
      IClass superclass = null;
      superclass = c.getSuperclass();
      if (superclass != null) {
        workingSuperclasses.remove(superclass);
        Node supernode = findOrCreateNode(superclass);
        if (DEBUG) {
          System.err.println(("addChild " + node.getJavaClass() + " to " + supernode.getJavaClass()));
        }
        supernode.addChild(node);
        if (supernode.getJavaClass().getReference().equals(rootTypeRef)) {
          node = null;
        } else {
          node = supernode;
        }
      } else {
        node = null;
      }
    }

    if (loadedSuperInterfaces != null) {
      for (Iterator it3 = loadedSuperInterfaces.iterator(); it3.hasNext();) {
        final IClass iface = (IClass) it3.next();
        try {
          // make sure we'll be able to load the interface!
          computeSuperclasses(iface);
        } catch (IllegalStateException e) {
          Warnings.add(ClassExclusion.create(iface.getReference(), e.getMessage()));
          continue;
        }
        if (!iface.isInterface()) {
          Warnings.add(new Warning() {
            
            @Override
            public String getMsg() {
              return "class implements non-interface " + iface.getReference() + " as an interface";
            }
          });
          continue;
        }
        recordImplements(klass, iface);
      }
    }
    return true;
  }

  /**
   * Record that a klass implements a particular interface
   */
  private void recordImplements(IClass klass, IClass iface) {
    Set impls = MapUtil.findOrCreateSet(implementors, iface);
    impls.add(klass);
  }

  /**
   * Find the possible targets of a call to a method reference. Note that if the reference is to an instance initialization method,
   * we assume the method was called with invokespecial rather than invokevirtual.
   * 
   * @param ref method reference
   * @return the set of IMethods that this call can resolve to.
   * @throws IllegalArgumentException if ref is null
   */
  public Collection getPossibleTargets(MethodReference ref) {
    if (ref == null) {
      throw new IllegalArgumentException("ref is null");
    }
    IClassLoader loader;
    try {
      loader = factory.getLoader(ref.getDeclaringClass().getClassLoader(), this, scope);
    } catch (IOException e) {
      throw new UnimplementedError("factory.getLoader failed " + e);
    }
    IClass declaredClass;
   * @param m
    declaredClass = loader.lookupClass(ref.getDeclaringClass().getName());
    if (declaredClass == null) {
      return Collections.emptySet();
    }
    Set targets = HashSetFactory.make();
    targets.addAll(findOrCreateTargetSet(declaredClass, ref));
    return (targets);
  }

  /**
   * Find the possible targets of a call to a method reference
   * 
   * @param ref method reference
   * @return the set of IMethods that this call can resolve to.
   */
  @SuppressWarnings("unchecked")
  private Set findOrCreateTargetSet(IClass declaredClass, MethodReference ref) {
    Map> classCache = (Map>) CacheReference.get(targetCache
        .get(declaredClass));
    if (classCache == null) {
      classCache = HashMapFactory.make(3);
      targetCache.put(declaredClass, CacheReference.make(classCache));
    }
    Set result = classCache.get(ref);
    if (result == null) {
      result = getPossibleTargets(declaredClass, ref);
      classCache.put(ref, result);
    }
    return result;
  }

  /**
   * Find the possible receivers of a call to a method reference
   * 
   * @param ref method reference
   * @return the set of IMethods that this call can resolve to.
   */
  public Set getPossibleTargets(IClass declaredClass, MethodReference ref) {

    if (ref.getName().equals(MethodReference.initAtom)) {
      // for an object init method, use the method alone as a possible target,
      // rather than inspecting subclasses
      IMethod resolvedMethod = resolveMethod(ref);
      assert resolvedMethod != null;
      return Collections.singleton(resolvedMethod);
    }
    if (declaredClass.isInterface()) {
      HashSet result = HashSetFactory.make(3);
      Set impls = implementors.get(declaredClass);
      if (impls == null) {
        // give up and return no receivers
        return Collections.emptySet();
      }
      for (Iterator it = impls.iterator(); it.hasNext();) {
        IClass klass = (IClass) it.next();
        if (!klass.isInterface() && !klass.isAbstract()) {
      return result;
          result.addAll(computeTargetsNotInterface(ref, klass));
        }
      }
      return result;
    } else {
      return computeTargetsNotInterface(ref, declaredClass);
    }

  }

  /**
   * Get the targets for a method ref invoked on a class klass. The klass had better not be an interface.
   * 
   * @param ref method to invoke
   * @param klass declaringClass of receiver
   * @return Set the set of method implementations that might receive the message
   */
  private Set computeTargetsNotInterface(MethodReference ref, IClass klass) {

    Node n = findNode(klass);
    HashSet result = HashSetFactory.make(3);
    // if n is null, then for some reason this class is excluded
    // from the analysis. Return a result of no targets.
    if (n == null)
      return result;

    Selector selector = ref.getSelector();

    // try to resolve the method by walking UP the class hierarchy
    IMethod resolved = resolveMethod(klass, selector);

    if (resolved != null) {
      result.add(resolved);
    }

    // find any receivers that override the method with inheritance
    result.addAll(computeOverriders(n, selector));

    return result;
  }

  /**
   * Return the unique receiver of an invocation of method on an object of type m.getDeclaredClass
   * @return IMethod, or null if no appropriate receiver is found.
   * @throws IllegalArgumentException if m is null
   */
  public IMethod resolveMethod(MethodReference m) {
    if (m == null) {
      throw new IllegalArgumentException("m is null");
    }
    IClass receiver = lookupClass(m.getDeclaringClass());
    if (receiver == null) {
      return null;
    }
    Selector selector = m.getSelector();
    return resolveMethod(receiver, selector);
  }

  /**
   * @return the canonical IField that represents a given field , or null if none found
   * @throws IllegalArgumentException if f is null
   */
  public IField resolveField(FieldReference f) {
    if (f == null) {
      throw new IllegalArgumentException("f is null");
    }
    IClass klass = lookupClass(f.getDeclaringClass());
    if (klass == null) {
      return null;
    }
    return resolveField(klass, f);
  }

  /**
   * @return the canonical IField that represents a given field , or null if none found
   * @throws IllegalArgumentException if f is null
   * @throws IllegalArgumentException if klass is null
   */
  public IField resolveField(IClass klass, FieldReference f) {
    if (klass == null) {
      throw new IllegalArgumentException("klass is null");
    }
    if (f == null) {
      throw new IllegalArgumentException("f is null");
    }
    return klass.getField(f.getName(), f.getFieldType().getName());
  }

  /**
   * Return the unique target of an invocation of method on an object of type declaringClass
   * 
   * @param receiverClass type of receiver
   * @param selector method signature
   * @return Method resolved method abstraction
   * @throws IllegalArgumentException if receiverClass is null
   */
  public IMethod resolveMethod(IClass receiverClass, Selector selector) {
    if (receiverClass == null) {
      throw new IllegalArgumentException("receiverClass is null");
    }
    IMethod result = findMethod(receiverClass, selector);
    if (result != null) {
    } else {
      IClass superclass = null;
      superclass = receiverClass.getSuperclass();
      if (superclass == null) {
        if (DEBUG) {
          System.err.println(("resolveMethod(" + selector + ") failed: method not found"));
        }
        return null;
      } else {
        if (DEBUG) {
          System.err.println(("Attempt to resolve for " + receiverClass + " in superclass: " + superclass + " " + selector));
        }
        return resolveMethod(superclass, selector);
      }
    }
  }

  /**
   * Does a particular class contain (implement) a particular method?
   * 
   * @param clazz class in question
   * @param selector method selector
   * @return the method if found, else null
   */
  private IMethod findMethod(IClass clazz, Selector selector) {
    return clazz.getMethod(selector);
  }

  /**
   * Get the set of subclasses of a class that provide implementations of a method
   * 
   * @param node abstraction of class in question
   * @param selector method signature
   * @return Set set of IMethods that override the method
   */
  private Set computeOverriders(Node node, Selector selector) {
    HashSet result = HashSetFactory.make(3);
    for (Iterator it = node.getChildren(); it.hasNext();) {

      Node child = it.next();
      IMethod m = findMethod(child.getJavaClass(), selector);
      if (m != null) {
        result.add(m);
      }
      result.addAll(computeOverriders(child, selector));
    }
    return result;
  }

  private Node findNode(IClass klass) {
    return map.get(klass.getReference());
  }

  private Node findOrCreateNode(IClass klass) {
    Node result = map.get(klass.getReference());
    if (result == null) {
      result = new Node(klass);
      map.put(klass.getReference(), result);
    }
    return result;
  }

  @Override
  public String toString() {
    StringBuffer result = new StringBuffer(100);
    recursiveStringify(root, result);
    return result.toString();
  }

  private void recursiveStringify(Node n, StringBuffer buffer) {
    buffer.append(n.toString()).append("\n");
    for (Iterator it = n.getChildren(); it.hasNext();) {
      Node child = it.next();
      recursiveStringify(child, buffer);
    }
  }

  /**
   * Number the class hierarchy tree to support efficient subclass tests. After numbering the tree, n1 is a child of n2 iff n2.left
   * <= n1.left ^ n1.left <= n2.right. Described as "relative numbering" by Vitek, Horspool, and Krall, OOPSLA 97
   * 
   * TODO: this implementation is recursive; un-recursify if needed
   */
  private int nextNumber = 1;

  private void numberTree() {
    assert root != null;
    visitForNumbering(root);
  }

  private void visitForNumbering(Node N) {
    N.left = nextNumber++;
    for (Iterator it = N.children.iterator(); it.hasNext();) {
      Node C = it.next();
      visitForNumbering(C);
    }
    N.right = nextNumber++;
  }

  /**
   * internal representation of a node in the class hiearachy, representing one java class.
   */
  private static final class Node {

    private final IClass klass;

    final private Set children = HashSetFactory.make(3);

    // the following two fields are used for efficient subclass tests.
    // After numbering the tree, n1 is a child of n2 iff
    // n2.left <= n1.left ^ n1.left <= n2.right.
    // Described as "relative numbering" by Vitek, Horspool, and Krall, OOPSLA
    // 97
    private int left = -1;

    private int right = -1;

    Node(IClass klass) {
      this.klass = klass;
    }

    boolean isInterface() {
      return klass.isInterface();
    }

    IClass getJavaClass() {
      return klass;
    }

    void addChild(Node child) {
      children.add(child);
    }

    Iterator getChildren() {
      return children.iterator();
    }

    @Override
    public String toString() {
      StringBuffer result = new StringBuffer(100);
      result.append(klass.toString()).append(":");
      for (Iterator i = children.iterator(); i.hasNext();) {
        Node n = i.next();
        result.append(n.klass.toString());
        if (i.hasNext())
          result.append(",");
      }
      return result.toString();
    }

    @Override
    public int hashCode() {
      return klass.hashCode() * 929;
    }

    @Override
    public boolean equals(Object obj) {
      return this == obj;
    }

  }

  public ClassLoaderFactory getFactory() {
    return factory;
  }

  /**
   * @throws IllegalArgumentException if A is null
   */
  public IClass getLeastCommonSuperclass(IClass a, IClass b) {
    assert (a.getClassLoader().getLanguage().equals(b.getClassLoader().getLanguage()));
    Language lang = a.getClassLoader().getLanguage();
    if (a == null) {
      throw new IllegalArgumentException("A is null");
    }
    TypeReference tempA = a.getReference();
    if (a.equals(b)) {
      return a;
    } else if (tempA.equals(TypeReference.Null)) {
      return b;
    } else if (b.getReference().equals(TypeReference.Null)) {
      return a;
    } else if (b.getReference().equals(lang.getRootType())) {
      return b;
    } else {
      Node n = map.get(b.getReference());
      if (n == null) {
        assert n != null : "null n for " + b;
      }
      Set superB;
      try {
        superB = getSuperclasses(b);
      } catch (ClassHierarchyException e1) {
        e1.printStackTrace();
        Assertions.UNREACHABLE();
        superB = null;
      }
      IClass aa = a;
      while (aa != null) {
        if (superB.contains(aa)) {
          return aa;
        }
        aa = aa.getSuperclass();
      }
      Set superA;
      try {
        superA = getSuperclasses(a);
      } catch (ClassHierarchyException e1) {
        e1.printStackTrace();
        Assertions.UNREACHABLE();
        superA = null;
      }
      Assertions.UNREACHABLE("getLeastCommonSuperclass " + tempA + " " + b + ": " + superA + ", " + superB);
      return null;
    }
  }

  private Set getSuperclasses(IClass c) throws ClassHierarchyException {
    HashSet result = HashSetFactory.make(3);
    while (c.getSuperclass() != null) {
      result.add(c.getSuperclass());
      c = c.getSuperclass();
    }
    return result;
  }

  /*
   * @see com.ibm.wala.ipa.cha.IClassHierarchy#getLeastCommonSuperclass(com.ibm.wala.types.TypeReference,
   * com.ibm.wala.types.TypeReference)
   */
  public TypeReference getLeastCommonSuperclass(TypeReference a, TypeReference b) {
    if (a == null) {
      throw new IllegalArgumentException("a is null");
    }
    if (a.equals(b))
      return a;
    IClass aClass = lookupClass(a);
    IClass bClass = lookupClass(b);
    if (aClass == null || bClass == null) {
      // One of the classes is not in scope. Give up.
      if (aClass != null) {
        return aClass.getClassLoader().getLanguage().getRootType();
      } else if (bClass != null) {
        return bClass.getClassLoader().getLanguage().getRootType();
      } else {
        return getRootClass().getReference();
      }
    }
    return getLeastCommonSuperclass(aClass, bClass).getReference();
  }

  /**
   * Find a class in this class hierarchy.
   * 
   * @return the {@link IClass} for a if found; null if can't find the class.
   * @throws IllegalArgumentException if A is null
   */
      if (n2 == null) {
  public IClass lookupClass(TypeReference a) {
    if (a == null) {
      throw new IllegalArgumentException("a is null");
    }
    ClassLoaderReference loader = a.getClassLoader();

    ClassLoaderReference parent = loader.getParent();
    // first delegate lookup to the parent loader.
    if (parent != null) {
      TypeReference p = TypeReference.findOrCreate(parent, a.getName());
      IClass c = lookupClass(p);
      if (c != null) {
        return c;
      }
    }

    // lookup in the parent failed. lookup based on the declared loader of a.
    if (a.isArrayType()) {
      TypeReference elt = a.getInnermostElementType();
      if (elt.isPrimitiveType()) {
        // look it up with the primordial loader.
        return getRootClass().getClassLoader().lookupClass(a.getName());
      } else {
        IClass c = lookupClass(elt);
        if (c == null) {
          // can't load the element class, so give up.
          return null;
        } else {
          // we know it comes from c's class loader.
          return c.getClassLoader().lookupClass(a.getName());
        }
      }
    } else {
      Node n = map.get(a);
      if (n != null) {
        return n.klass;
      } else {
        return null;
      }
    }
  }

  private boolean slowIsSubclass(IClass sub, IClass sup) {
    if (sub == sup) {
      return true;
    } else {
      IClass parent = sub.getSuperclass();
      if (parent == null) {
        return false;
      } else {
        return slowIsSubclass(parent, sup);
      }
    }
  }

  /**
   * Is c a subclass of T?
   * 
   * @throws IllegalArgumentException if c is null
   */
  public boolean isSubclassOf(IClass c, IClass t) {
    if (c == null) {
      throw new IllegalArgumentException("c is null");
    }
    assert t != null : "null T";

    if (c.isArrayClass()) {
      if (t.getReference() == TypeReference.JavaLangObject) {
        return true;
      } else if (t.getReference().isArrayType()) {
        TypeReference elementType = t.getReference().getArrayElementType();
        if (elementType.isPrimitiveType()) {
          return elementType.equals(c.getReference().getArrayElementType());
        } else {
          IClass elementKlass = lookupClass(elementType);
          if (elementKlass == null) {
            // uh oh.
            Warnings.add(ClassHierarchyWarning.create("could not find " + elementType));
            return false;
          }
          IClass ce = ((ArrayClass) c).getElementClass();
          if (ce == null) {
            return false;
          }
          if (elementKlass.isInterface()) {
            return implementsInterface(ce, elementKlass);
          } else {
            return isSubclassOf(ce, elementKlass);
          }
        }
      } else {
        return false;
      }
    } else {
      if (t.getReference().isArrayType()) {
        return false;
      }
      if (c.getReference().equals(t.getReference())) {
        return true;
      }
      Node n1 = map.get(c.getReference());
      if (n1 == null) {
        // some wacky case, like a FakeRootClass
        return false;
      }
      Node n2 = map.get(t.getReference());
        // some wacky case, like a FakeRootClass
        return false;
      }
      if (n1.left == -1) {
        return slowIsSubclass(c, t);
      } else if (n2.left == -1) {
        return slowIsSubclass(c, t);
      } else {
        return (n2.left <= n1.left) && (n1.left <= n2.right);
      }
    }
  }

  /**
   * Does c implement i?
   * 
   * @return true iff i is an interface and c is a class that implements i, or c is an interface that extends i.
   */
  public boolean implementsInterface(IClass c, IClass i) {
    if (i == null) {
      throw new IllegalArgumentException("Cannot ask implementsInterface with i == null");
    }
    if (c == null) {
      throw new IllegalArgumentException("Cannot ask implementsInterface with c == null");
    }
    if (!i.isInterface()) {
      return false;
    }
    if (c.equals(i)) {
      return true;
    }
    if (c.isArrayClass()) {
      // arrays implement Cloneable and Serializable
      return i.equals(lookupClass(TypeReference.JavaLangCloneable)) || i.equals(lookupClass(TypeReference.JavaIoSerializable));
    }
    Set impls = implementors.get(i);
    if (impls != null && impls.contains(c)) {
      return true;
    }
    return false;
  }

  /**
   * Return set of all subclasses of type in the Class Hierarchy TODO: Tune this implementation. Consider caching if necessary.
   */
  public Collection computeSubClasses(TypeReference type) {
    IClass t = lookupClass(type);
    if (t == null) {
      throw new IllegalArgumentException("could not find class for TypeReference " + type);
    }
    // a hack: TODO: work on better caching
    if (t.getReference().equals(TypeReference.JavaLangError)) {
      if (subclassesOfError == null) {
        subclassesOfError = computeSubClassesInternal(t);
      }
      return subclassesOfError;
    } else if (t.getReference().equals(TypeReference.JavaLangRuntimeException)) {
      if (runtimeExceptionClasses == null) {
        runtimeExceptionClasses = computeSubClassesInternal(t);
      }
      return runtimeExceptionClasses;
    } else {
      return computeSubClassesInternal(t);
    }
  }

  /**
   * Solely for optimization; return a Collection representing the subclasses of Error
   * 
   * kind of ugly. a better scheme?
   */
  public Collection getJavaLangErrorTypes() {
    if (subTypeRefsOfError == null) {
      computeSubClasses(TypeReference.JavaLangError);
      subTypeRefsOfError = HashSetFactory.make(subclassesOfError.size());
      for (Iterator it = subclassesOfError.iterator(); it.hasNext();) {
        IClass klass = (IClass) it.next();
        subTypeRefsOfError.add(klass.getReference());
      }
    }
    return Collections.unmodifiableCollection(subTypeRefsOfError);
  }

  /**
   * Solely for optimization; return a Collection representing the subclasses of RuntimeException
   * 
   * kind of ugly. a better scheme?
   */
  public Collection getJavaLangRuntimeExceptionTypes() {
    if (runtimeExceptionTypeRefs == null) {
      computeSubClasses(TypeReference.JavaLangRuntimeException);
      runtimeExceptionTypeRefs = HashSetFactory.make(runtimeExceptionClasses.size());
      for (Iterator it = runtimeExceptionClasses.iterator(); it.hasNext();) {
        IClass klass = (IClass) it.next();
        runtimeExceptionTypeRefs.add(klass.getReference());
      }
    }
    return Collections.unmodifiableCollection(runtimeExceptionTypeRefs);
  }

  /**
   * Return set of all subclasses of type in the Class Hierarchy TODO: Tune this implementation. Consider caching if necessary.
   * 
   * @return Set of IClasses
   */
  private Set computeSubClassesInternal(IClass T) {
    if (T.isArrayClass()) {
      return Collections.singleton(T);
    }
    Node node = findNode(T);
    assert node != null : "null node for class " + T;
    HashSet result = HashSetFactory.make(3);
    result.add(T);
    for (Iterator it = node.getChildren(); it.hasNext();) {
      Node child = it.next();
      result.addAll(computeSubClasses(child.klass.getReference()));
    }
    return result;
  }

  public boolean isInterface(TypeReference type) {
    IClass T = lookupClass(type);
    assert T != null : "Null lookup for " + type;
    return T.isInterface();
  }

  /**
   * TODO: tune this if necessary
   * 
   * @param type an interface
   * @return Set of IClass that represent implementors of the interface
   */
  public Set getImplementors(TypeReference type) {
    IClass T = lookupClass(type);
    Set result = implementors.get(T);
    if (result == null) {
      return Collections.emptySet();
    }
    return Collections.unmodifiableSet(result);
  }

  public Iterator iterator() {
    Function toClass = new Function() {
      public IClass apply(Node n) {
        return n.klass;
      }
    };
    return new MapIterator(map.values().iterator(), toClass);
  }

  /**
   * @return The number of classes present in the class hierarchy.
   */
  public int getNumberOfClasses() {
    return map.keySet().size();
  }

  public IClassLoader[] getLoaders() {
    return loaders;
  }

  public IClassLoader getLoader(ClassLoaderReference loaderRef) {
    for (int i = 0; i < loaders.length; i++) {
      if (loaders[i].getReference().equals(loaderRef)) {
        return loaders[i];
      }
    }
    Assertions.UNREACHABLE();
    return null;
  }

  public AnalysisScope getScope() {
    return scope;
  }

  /**
   * @return the number of classes that immediately extend klass. if klass is an array class A[][]...[], we return number of
   *         immediate subclasses of A. If A is primitive, we return 0.
   */
  public int getNumberOfImmediateSubclasses(IClass klass) {
    if (klass.isArrayClass()) {
      IClass innermost = getInnermostTypeOfArrayClass(klass);
      return innermost == null ? 0 : getNumberOfImmediateSubclasses(innermost);
    }
    Node node = findNode(klass);
    return node.children.size();
  }

  /**
   * @param klass
   * @return the classes that immediately extend klass. if klass is an array class A[][]...[], we return array classes B[][]...[]
   *         (same dimensionality) where B is an immediate subclass of A. If A is primitive, we return the empty set.
   */
  public Collection getImmediateSubclasses(IClass klass) {
    if (klass.isArrayClass()) {
      return getImmediateArraySubclasses((ArrayClass)klass);
    }
    Function node2Class = new Function() {
      public IClass apply(Node n) {
        return n.klass;
      }
    };
    return Iterator2Collection.toSet(new MapIterator(findNode(klass).children.iterator(), node2Class));
  }

  private Collection getImmediateArraySubclasses(ArrayClass klass) {
    IClass innermost = getInnermostTypeOfArrayClass(klass);
    if (innermost == null) {
      return Collections.emptySet();
    }
    Collection innermostSubclasses = getImmediateSubclasses(innermost);
    int dim = klass.getDimensionality();
    Collection result = HashSetFactory.make();
    for (IClass k : innermostSubclasses) {
      TypeReference ref = k.getReference();
      for (int i = 0; i < dim; i++) {
        ref = ref.getArrayTypeForElementType();
      }
      result.add(lookupClass(ref));
    }
    return result;
  }

  /**
   * for an array class, get the innermost type, or null if it's primitive
   */
  private IClass getInnermostTypeOfArrayClass(IClass klass) {
    TypeReference result = klass.getReference();
    while (result.isArrayType()) {
      result = result.getArrayElementType();
    }
    return result.isPrimitiveType() ? null : lookupClass(result);
  }

  /**
   * @return a ClassHierarchy object representing the analysis scope
   * @throws ClassHierarchyException
   */
  public static ClassHierarchy make(AnalysisScope scope) throws ClassHierarchyException {
    if (scope == null) {
      throw new IllegalArgumentException("null scope");
    }
    return make(scope, new ClassLoaderFactoryImpl(scope.getExclusions()));
  }

  /**
   * temporarily marking this internal to avoid infinite sleep with randomly chosen IProgressMonitor.
   */
  public static ClassHierarchy make(AnalysisScope scope, IProgressMonitor monitor) throws ClassHierarchyException {
    if (scope == null) {
      throw new IllegalArgumentException("null scope");
    }
    return make(scope, new ClassLoaderFactoryImpl(scope.getExclusions()), monitor);
  }

  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory) throws ClassHierarchyException {
    if (scope == null) {
      throw new IllegalArgumentException("null scope");
    }
    if (factory == null) {
      throw new IllegalArgumentException("null factory");
    }
    return new ClassHierarchy(scope, factory, null);
  }

  /**
   * temporarily marking this internal to avoid infinite sleep with randomly chosen IProgressMonitor.
   */
  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, IProgressMonitor monitor)
      throws ClassHierarchyException {
    return new ClassHierarchy(scope, factory, monitor);
  }

  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, Set languages)
      throws ClassHierarchyException {
    return new ClassHierarchy(scope, factory, languages, null);
  }

  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, Language language)
      throws ClassHierarchyException {
    return new ClassHierarchy(scope, factory, language, null);
  }

  /**
   * temporarily marking this internal to avoid infinite sleep with randomly chosen IProgressMonitor. TODO: nanny for testgen
   */
  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, Language language, IProgressMonitor monitor)
      throws ClassHierarchyException {
    if (factory == null) {
      throw new IllegalArgumentException("null factory");
    }
    return new ClassHierarchy(scope, factory, language, monitor);
  }

  public IClass getRootClass() {
    return root.getJavaClass();
  }

  public boolean isRootClass(IClass c) throws IllegalArgumentException {
    if (c == null) {
      throw new IllegalArgumentException("c == null");
    }
    return c.equals(root.getJavaClass());
  }

  public int getNumber(IClass c) {
    return map.get(c.getReference()).left;
  }

  /**
   * A warning for when we fail to resolve the type for a checkcast
   */
  private static class ClassExclusion extends Warning {

    final TypeReference klass;

    final String message;

    ClassExclusion(TypeReference klass, String message) {
      super(Warning.MODERATE);
      this.klass = klass;
      this.message = message;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + klass + " " + message;
    }

    public static ClassExclusion create(TypeReference klass, String message) {
      return new ClassExclusion(klass, message);
    }
  }

  /**
   * Does an expression c1 x := c2 y typecheck?
   * 
   * i.e. is c2 a subtype of c1?
   * 
   * @throws IllegalArgumentException if c1 is null
   * @throws IllegalArgumentException if c2 is null
   */
  public boolean isAssignableFrom(IClass c1, IClass c2) {
    if (c2 == null) {
      throw new IllegalArgumentException("c2 is null");
    }
    if (c1 == null) {
      throw new IllegalArgumentException("c1 is null");
    }
    if (c1.isInterface()) {
      return implementsInterface(c2, c1);
    } else {
      if (c2.isInterface()) {
        return c1.equals(getRootClass());
      } else {
        return isSubclassOf(c2, c1);
      }
    }
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.cha;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.ibm.wala.classLoader.ArrayClass;
import com.ibm.wala.classLoader.ClassLoaderFactory;
import com.ibm.wala.classLoader.ClassLoaderFactoryImpl;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IClassLoader;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.ShrikeClass;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.MapIterator;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.functions.Function;
import com.ibm.wala.util.ref.CacheReference;
import com.ibm.wala.util.ref.ReferenceCleanser;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.Warnings;

/**
 * Simple implementation of a class hierarchy.
 * 
 * Note that this class hierarchy implementation is mutable. You can add classes via addClass(). You can add a class even if
 * c.getClassLoader() does not appear in getLoaders().
 */
public class ClassHierarchy implements IClassHierarchy {

  private static final boolean DEBUG = false;

  /**
   * Languages that contribute classes to the set represented in this hierarchy. The languages may for example be related by
   * inheritance (e.g. X10 derives from Java, and shares a common type hierarchy rooted at java.lang.Object).
   */
  private final Set languages = HashSetFactory.make();

  /**
   * For each {@link IClass} c in this class hierarchy, this map maps c.getReference() to the {@link Node}
   * 
   * Note that this class provides an iterator() over this map, and that some WALA utilities (e.g. ReferenceCleanser) must iterate
   * over all classes. But also note that the class hierarchy is mutable (addClass()). So, when trying to run multiple threads, we
   * could see a race condition between iterator() and addClass(). With a normal {@link HashMap}, this would result in a
   * {@link ConcurrentModificationException}. But with a {@link ConcurrentHashMap}, at least the code merrily chugs along,
   * tolerating the race.
   */
  final private Map map = new ConcurrentHashMap();

  /**
   * {@link TypeReference} for the root type
   */
  private TypeReference rootTypeRef;

  /**
   * root node of the class hierarchy
   */
  private Node root;

  /**
   * An object which defines class loaders.
   */
  final private ClassLoaderFactory factory;

  /**
   * The loaders used to define this class hierarchy.
   */
  final private IClassLoader[] loaders;

  /**
   * A mapping from IClass -> Selector -> Set of IMethod
   */
  final private HashMap targetCache = HashMapFactory.make();

  /**
   * Governing analysis scope
   */
  private final AnalysisScope scope;

  /**
   * A mapping from IClass (representing an interface) -> Set of IClass that implement that interface
   */
  private final Map> implementors = HashMapFactory.make();

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection subclassesOfError;

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection subTypeRefsOfError;

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection runtimeExceptionClasses;

  /**
   * A temporary hack : TODO: do intelligent caching somehow
   */
  private Collection runtimeExceptionTypeRefs;

  /**
   * Return a set of {@link IClass} that holds all superclasses of klass
   * 
   * @param klass class in question
   * @return Set the result set
   */
  private Set computeSuperclasses(IClass klass) {
    if (DEBUG) {
      System.err.println("computeSuperclasses: " + klass);
    }

    Set result = HashSetFactory.make(3);

    klass = klass.getSuperclass();

    while (klass != null) {
      if (DEBUG) {
        System.err.println("got superclass " + klass);
      }
      boolean added = result.add(klass);
      if (!added) {
        // oops.  we have A is a sub-class of B and B is a sub-class of A.  blow up.
        throw new IllegalStateException("cycle in the extends relation for class " + klass);
      }
      klass = klass.getSuperclass();
      if (klass != null && klass.getReference().getName().equals(rootTypeRef.getName())) {
        if (!klass.getReference().getClassLoader().equals(rootTypeRef.getClassLoader())) {
          throw new IllegalStateException("class " + klass + " is invalid, unexpected classloader");
        }
      }
    }
    return result;
  }

  private ClassHierarchy(AnalysisScope scope, ClassLoaderFactory factory, Language language, IProgressMonitor progressMonitor)
      throws ClassHierarchyException, IllegalArgumentException {
    this(scope, factory, Collections.singleton(language), progressMonitor);
  }

  private ClassHierarchy(AnalysisScope scope, ClassLoaderFactory factory, IProgressMonitor progressMonitor)
      throws ClassHierarchyException, IllegalArgumentException {
    this(scope, factory, scope.getLanguages(), progressMonitor);
  }

  private ClassHierarchy(AnalysisScope scope, ClassLoaderFactory factory, Collection languages,
      IProgressMonitor progressMonitor) throws ClassHierarchyException, IllegalArgumentException {
    // now is a good time to clear the warnings globally.
    // TODO: think of a better way to guard against warning leaks.
    Warnings.clear();

    if (factory == null) {
      throw new IllegalArgumentException();
    }
    if (scope.getLanguages().size() == 0) {
      throw new IllegalArgumentException("AnalysisScope must contain at least 1 language");
    }
    this.scope = scope;
    this.factory = factory;
    Set langNames = HashSetFactory.make();
    for (Language lang : languages) {
      this.languages.add(lang);
      this.languages.addAll(lang.getDerivedLanguages());
      langNames.add(lang.getName());
    }
    for (Language lang : this.languages) {
      if (lang.getRootType() != null && lang.getRootType() != this.rootTypeRef) {
        if (this.rootTypeRef != null) {
          throw new IllegalArgumentException("AnalysisScope must have only 1 root type: " + lang.getRootType() + ", " + rootTypeRef);
        } else {
          this.rootTypeRef = lang.getRootType();
        }
      }
    }
    try {
      int numLoaders = 0;
      for (ClassLoaderReference ref : scope.getLoaders()) {
        if (langNames.contains(ref.getLanguage())) {
          numLoaders++;
        }
      }

      loaders = new IClassLoader[numLoaders];
      int idx = 0;

      if (progressMonitor != null) {
        progressMonitor.beginTask("Build Class Hierarchy", numLoaders);
      }
      for (ClassLoaderReference ref : scope.getLoaders()) {
        if (progressMonitor != null) {
          if (progressMonitor.isCanceled()) {
            throw new CancelCHAConstructionException();
          }
        }

        if (langNames.contains(ref.getLanguage())) {
          IClassLoader icl = factory.getLoader(ref, this, scope);
          loaders[idx++] = icl;

          if (progressMonitor != null) {
            progressMonitor.worked(1);
          }
        }
      }

      for (IClassLoader icl : loaders) {
        addAllClasses(icl, progressMonitor);

        if (progressMonitor != null) {
          progressMonitor.worked(1);
        }
      }

    } catch (IOException e) {
      throw new ClassHierarchyException("factory.getLoader failed " + e);
    } finally {
      if (progressMonitor != null) {
        progressMonitor.done(); // In case an exception is thrown.
      }
    }

    if (root == null) {
      throw new ClassHierarchyException("failed to load root " + rootTypeRef + " of class hierarchy");
    }

    // perform numbering for subclass tests.
    numberTree();
    ReferenceCleanser.registerClassHierarchy(this);
  }

  /**
   * Add all classes in a class loader to the hierarchy.
   */
  private void addAllClasses(IClassLoader loader, IProgressMonitor progressMonitor) throws CancelCHAConstructionException {
    if (DEBUG) {
      System.err.println(("Add all classes from loader " + loader));
    }
    Collection toRemove = HashSetFactory.make();
    for (Iterator it = loader.iterateAllClasses(); it.hasNext();) {
      if (progressMonitor != null) {
        if (progressMonitor.isCanceled()) {
          throw new CancelCHAConstructionException();
        }
      }
      IClass klass = it.next();
      boolean added = addClass(klass);
      if (!added) {
        toRemove.add(klass);
      }
    }
    loader.removeAll(toRemove);

  }

  /**
   * @return true if the add succeeded; false if it failed for some reason
   * @throws IllegalArgumentException if klass is null
   */
  public boolean addClass(IClass klass) {
    if (klass == null) {
      throw new IllegalArgumentException("klass is null");
    }
    if (klass.getReference().getName().equals(rootTypeRef.getName())) {
      if (!klass.getReference().getClassLoader().equals(rootTypeRef.getClassLoader())) {
        throw new IllegalArgumentException("class " + klass + " is invalid, unexpected classloader");
      }
    }
    if (DEBUG) {
      System.err.println(("Attempt to add class " + klass));
    }
    Set loadedSuperclasses;
    Collection loadedSuperInterfaces;
    try {
      loadedSuperclasses = computeSuperclasses(klass);
      loadedSuperInterfaces = klass.getAllImplementedInterfaces();
    } catch (Exception e) {
      // a little cleanup
      if (klass instanceof ShrikeClass) {
        if (DEBUG) {
          System.err.println(("Exception.  Clearing " + klass));
        }
      }
      Warnings.add(ClassExclusion.create(klass.getReference(), e.getMessage()));
      return false;
    }
    Node node = findOrCreateNode(klass);

    if (klass.getReference().equals(this.rootTypeRef)) {
      // there is only one root
      assert root == null;
      root = node;
    }

    Set workingSuperclasses = HashSetFactory.make(loadedSuperclasses);
    while (node != null) {
      IClass c = node.getJavaClass();
      IClass superclass = null;
      superclass = c.getSuperclass();
      if (superclass != null) {
        workingSuperclasses.remove(superclass);
        Node supernode = findOrCreateNode(superclass);
        if (DEBUG) {
          System.err.println(("addChild " + node.getJavaClass() + " to " + supernode.getJavaClass()));
        }
        supernode.addChild(node);
        if (supernode.getJavaClass().getReference().equals(rootTypeRef)) {
          node = null;
        } else {
          node = supernode;
        }
      } else {
        node = null;
      }
    }

    if (loadedSuperInterfaces != null) {
      for (Iterator it3 = loadedSuperInterfaces.iterator(); it3.hasNext();) {
        final IClass iface = (IClass) it3.next();
        try {
          // make sure we'll be able to load the interface!
          computeSuperclasses(iface);
        } catch (IllegalStateException e) {
          Warnings.add(ClassExclusion.create(iface.getReference(), e.getMessage()));
          continue;
        }
        if (!iface.isInterface()) {
          Warnings.add(new Warning() {
            
            @Override
            public String getMsg() {
              return "class implements non-interface " + iface.getReference() + " as an interface";
            }
          });
          continue;
        }
        recordImplements(klass, iface);
      }
    }
    return true;
  }

  /**
   * Record that a klass implements a particular interface
   */
  private void recordImplements(IClass klass, IClass iface) {
    Set impls = MapUtil.findOrCreateSet(implementors, iface);
    impls.add(klass);
  }

  /**
   * Find the possible targets of a call to a method reference. Note that if the reference is to an instance initialization method,
   * we assume the method was called with invokespecial rather than invokevirtual.
   * 
   * @param ref method reference
   * @return the set of IMethods that this call can resolve to.
   * @throws IllegalArgumentException if ref is null
   */
  public Collection getPossibleTargets(MethodReference ref) {
    if (ref == null) {
      throw new IllegalArgumentException("ref is null");
    }
    IClassLoader loader;
    try {
      loader = factory.getLoader(ref.getDeclaringClass().getClassLoader(), this, scope);
    } catch (IOException e) {
      throw new UnimplementedError("factory.getLoader failed " + e);
    }
    IClass declaredClass;
    declaredClass = loader.lookupClass(ref.getDeclaringClass().getName());
    if (declaredClass == null) {
      return Collections.emptySet();
    }
    Set targets = HashSetFactory.make();
    targets.addAll(findOrCreateTargetSet(declaredClass, ref));
    return (targets);
  }

  /**
   * Find the possible targets of a call to a method reference
   * 
   * @param ref method reference
   * @return the set of IMethods that this call can resolve to.
   */
  @SuppressWarnings("unchecked")
  private Set findOrCreateTargetSet(IClass declaredClass, MethodReference ref) {
    Map> classCache = (Map>) CacheReference.get(targetCache
        .get(declaredClass));
    if (classCache == null) {
      classCache = HashMapFactory.make(3);
      targetCache.put(declaredClass, CacheReference.make(classCache));
    }
    Set result = classCache.get(ref);
    if (result == null) {
      result = getPossibleTargets(declaredClass, ref);
      classCache.put(ref, result);
    }
    return result;
  }

  /**
   * Find the possible receivers of a call to a method reference
   * 
   * @param ref method reference
   * @return the set of IMethods that this call can resolve to.
   */
  public Set getPossibleTargets(IClass declaredClass, MethodReference ref) {

    if (ref.getName().equals(MethodReference.initAtom)) {
      // for an object init method, use the method alone as a possible target,
      // rather than inspecting subclasses
      IMethod resolvedMethod = resolveMethod(ref);
      assert resolvedMethod != null;
      return Collections.singleton(resolvedMethod);
    }
    if (declaredClass.isInterface()) {
      HashSet result = HashSetFactory.make(3);
      Set impls = implementors.get(declaredClass);
      if (impls == null) {
        // give up and return no receivers
        return Collections.emptySet();
      }
      for (Iterator it = impls.iterator(); it.hasNext();) {
        IClass klass = (IClass) it.next();
        if (!klass.isInterface() && !klass.isAbstract()) {
          result.addAll(computeTargetsNotInterface(ref, klass));
        }
      }
      return result;
    } else {
      return computeTargetsNotInterface(ref, declaredClass);
    }

  }

  /**
   * Get the targets for a method ref invoked on a class klass. The klass had better not be an interface.
   * 
   * @param ref method to invoke
   * @param klass declaringClass of receiver
   * @return Set the set of method implementations that might receive the message
   */
  private Set computeTargetsNotInterface(MethodReference ref, IClass klass) {

    Node n = findNode(klass);
    HashSet result = HashSetFactory.make(3);
    // if n is null, then for some reason this class is excluded
    // from the analysis. Return a result of no targets.
    if (n == null)
      return result;

    Selector selector = ref.getSelector();

    // try to resolve the method by walking UP the class hierarchy
    IMethod resolved = resolveMethod(klass, selector);

    if (resolved != null) {
      result.add(resolved);
    }

    // find any receivers that override the method with inheritance
    result.addAll(computeOverriders(n, selector));

    return result;
  }

  /**
   * Return the unique receiver of an invocation of method on an object of type m.getDeclaredClass
   * 
   * @param m
   * @return IMethod, or null if no appropriate receiver is found.
   * @throws IllegalArgumentException if m is null
   */
  public IMethod resolveMethod(MethodReference m) {
    if (m == null) {
      throw new IllegalArgumentException("m is null");
    }
    IClass receiver = lookupClass(m.getDeclaringClass());
    if (receiver == null) {
      return null;
    }
    Selector selector = m.getSelector();
    return resolveMethod(receiver, selector);
  }

  /**
   * @return the canonical IField that represents a given field , or null if none found
   * @throws IllegalArgumentException if f is null
   */
  public IField resolveField(FieldReference f) {
    if (f == null) {
      throw new IllegalArgumentException("f is null");
    }
    IClass klass = lookupClass(f.getDeclaringClass());
    if (klass == null) {
      return null;
    }
    return resolveField(klass, f);
  }

  /**
   * @return the canonical IField that represents a given field , or null if none found
   * @throws IllegalArgumentException if f is null
   * @throws IllegalArgumentException if klass is null
   */
  public IField resolveField(IClass klass, FieldReference f) {
    if (klass == null) {
      throw new IllegalArgumentException("klass is null");
    }
    if (f == null) {
      throw new IllegalArgumentException("f is null");
    }
    return klass.getField(f.getName(), f.getFieldType().getName());
  }

  /**
   * Return the unique target of an invocation of method on an object of type declaringClass
   * 
   * @param receiverClass type of receiver
   * @param selector method signature
   * @return Method resolved method abstraction
   * @throws IllegalArgumentException if receiverClass is null
   */
  public IMethod resolveMethod(IClass receiverClass, Selector selector) {
    if (receiverClass == null) {
      throw new IllegalArgumentException("receiverClass is null");
    }
    IMethod result = findMethod(receiverClass, selector);
    if (result != null) {
      return result;
    } else {
      IClass superclass = null;
      superclass = receiverClass.getSuperclass();
      if (superclass == null) {
        if (DEBUG) {
          System.err.println(("resolveMethod(" + selector + ") failed: method not found"));
        }
        return null;
      } else {
        if (DEBUG) {
          System.err.println(("Attempt to resolve for " + receiverClass + " in superclass: " + superclass + " " + selector));
        }
        return resolveMethod(superclass, selector);
      }
    }
  }

  /**
   * Does a particular class contain (implement) a particular method?
   * 
   * @param clazz class in question
   * @param selector method selector
   * @return the method if found, else null
   */
  private IMethod findMethod(IClass clazz, Selector selector) {
    return clazz.getMethod(selector);
  }

  /**
   * Get the set of subclasses of a class that provide implementations of a method
   * 
   * @param node abstraction of class in question
   * @param selector method signature
   * @return Set set of IMethods that override the method
   */
  private Set computeOverriders(Node node, Selector selector) {
    HashSet result = HashSetFactory.make(3);
    for (Iterator it = node.getChildren(); it.hasNext();) {

      Node child = it.next();
      IMethod m = findMethod(child.getJavaClass(), selector);
      if (m != null) {
        result.add(m);
      }
      result.addAll(computeOverriders(child, selector));
    }
    return result;
  }

  private Node findNode(IClass klass) {
    return map.get(klass.getReference());
  }

    return factory;
  private Node findOrCreateNode(IClass klass) {
    Node result = map.get(klass.getReference());
    if (result == null) {
      result = new Node(klass);
      map.put(klass.getReference(), result);
    }
    return result;
  }

  @Override
  public String toString() {
    StringBuffer result = new StringBuffer(100);
    recursiveStringify(root, result);
    return result.toString();
  }

  private void recursiveStringify(Node n, StringBuffer buffer) {
    buffer.append(n.toString()).append("\n");
    for (Iterator it = n.getChildren(); it.hasNext();) {
      Node child = it.next();
      recursiveStringify(child, buffer);
    }
  }

  /**
   * Number the class hierarchy tree to support efficient subclass tests. After numbering the tree, n1 is a child of n2 iff n2.left
   * <= n1.left ^ n1.left <= n2.right. Described as "relative numbering" by Vitek, Horspool, and Krall, OOPSLA 97
   * 
   * TODO: this implementation is recursive; un-recursify if needed
   */
  private int nextNumber = 1;

  private void numberTree() {
    assert root != null;
    visitForNumbering(root);
  }

  private void visitForNumbering(Node N) {
    N.left = nextNumber++;
    for (Iterator it = N.children.iterator(); it.hasNext();) {
      Node C = it.next();
      visitForNumbering(C);
    }
    N.right = nextNumber++;
  }

  /**
   * internal representation of a node in the class hiearachy, representing one java class.
   */
  private static final class Node {

    private final IClass klass;

    final private Set children = HashSetFactory.make(3);

    // the following two fields are used for efficient subclass tests.
    // After numbering the tree, n1 is a child of n2 iff
    // n2.left <= n1.left ^ n1.left <= n2.right.
    // Described as "relative numbering" by Vitek, Horspool, and Krall, OOPSLA
    // 97
    private int left = -1;

    private int right = -1;

    Node(IClass klass) {
      this.klass = klass;
    }

    boolean isInterface() {
      return klass.isInterface();
    }

    IClass getJavaClass() {
      return klass;
    }

    void addChild(Node child) {
      children.add(child);
    }

    Iterator getChildren() {
      return children.iterator();
    }

    @Override
    public String toString() {
      StringBuffer result = new StringBuffer(100);
      result.append(klass.toString()).append(":");
      for (Iterator i = children.iterator(); i.hasNext();) {
        Node n = i.next();
        result.append(n.klass.toString());
        if (i.hasNext())
          result.append(",");
      }
      return result.toString();
    }

    @Override
    public int hashCode() {
      return klass.hashCode() * 929;
    }

    @Override
    public boolean equals(Object obj) {
      return this == obj;
    }

  }

  public ClassLoaderFactory getFactory() {
  }

  /**
   * @throws IllegalArgumentException if A is null
   */
  public IClass getLeastCommonSuperclass(IClass a, IClass b) {
    assert (a.getClassLoader().getLanguage().equals(b.getClassLoader().getLanguage()));
    Language lang = a.getClassLoader().getLanguage();
    if (a == null) {
      throw new IllegalArgumentException("A is null");
    }
    TypeReference tempA = a.getReference();
    if (a.equals(b)) {
      return a;
    } else if (tempA.equals(TypeReference.Null)) {
      return b;
    if (i == null) {
    } else if (b.getReference().equals(TypeReference.Null)) {
      return a;
    } else if (b.getReference().equals(lang.getRootType())) {
      return b;
    } else {
      Node n = map.get(b.getReference());
      if (n == null) {
        assert n != null : "null n for " + b;
      }
      Set superB;
      try {
        superB = getSuperclasses(b);
      } catch (ClassHierarchyException e1) {
        e1.printStackTrace();
        Assertions.UNREACHABLE();
        superB = null;
      }
      IClass aa = a;
      while (aa != null) {
        if (superB.contains(aa)) {
          return aa;
        }
        aa = aa.getSuperclass();
      }
      Set superA;
      try {
        superA = getSuperclasses(a);
      } catch (ClassHierarchyException e1) {
        e1.printStackTrace();
        Assertions.UNREACHABLE();
        superA = null;
      }
      Assertions.UNREACHABLE("getLeastCommonSuperclass " + tempA + " " + b + ": " + superA + ", " + superB);
      return null;
    }
  }

  private Set getSuperclasses(IClass c) throws ClassHierarchyException {
    HashSet result = HashSetFactory.make(3);
    while (c.getSuperclass() != null) {
      result.add(c.getSuperclass());
      c = c.getSuperclass();
    }
    return result;
  }

  /*
   * @see com.ibm.wala.ipa.cha.IClassHierarchy#getLeastCommonSuperclass(com.ibm.wala.types.TypeReference,
   * com.ibm.wala.types.TypeReference)
   */
  public TypeReference getLeastCommonSuperclass(TypeReference a, TypeReference b) {
    if (a == null) {
      throw new IllegalArgumentException("a is null");
    }
    if (a.equals(b))
      return a;
    IClass aClass = lookupClass(a);
    IClass bClass = lookupClass(b);
    if (aClass == null || bClass == null) {
      // One of the classes is not in scope. Give up.
      if (aClass != null) {
        return aClass.getClassLoader().getLanguage().getRootType();
      } else if (bClass != null) {
        return bClass.getClassLoader().getLanguage().getRootType();
      } else {
        return getRootClass().getReference();
      }
    }
    return getLeastCommonSuperclass(aClass, bClass).getReference();
  }

  /**
   * Find a class in this class hierarchy.
   * 
   * @return the {@link IClass} for a if found; null if can't find the class.
   * @throws IllegalArgumentException if A is null
   */
  public IClass lookupClass(TypeReference a) {
    if (a == null) {
      throw new IllegalArgumentException("a is null");
    }
    ClassLoaderReference loader = a.getClassLoader();

    ClassLoaderReference parent = loader.getParent();
    // first delegate lookup to the parent loader.
    if (parent != null) {
      TypeReference p = TypeReference.findOrCreate(parent, a.getName());

      IClass c = lookupClass(p);
      if (c != null) {
        return c;
      }
    }

    // lookup in the parent failed. lookup based on the declared loader of a.
    if (a.isArrayType()) {
      TypeReference elt = a.getInnermostElementType();
      if (elt.isPrimitiveType()) {
        // look it up with the primordial loader.
        return getRootClass().getClassLoader().lookupClass(a.getName());
      } else {
        IClass c = lookupClass(elt);
        if (c == null) {
    return result;
          // can't load the element class, so give up.
/** BEGIN Custom change: remember unresolved classes */
          unresolved.add(elt);
/** END Custom change: remember unresolved classes */
          return null;
        } else {
          // we know it comes from c's class loader.
          return c.getClassLoader().lookupClass(a.getName());
        }
      }
    } else {
      Node n = map.get(a);
      if (n != null) {
        return n.klass;
      } else {
/** BEGIN Custom change: remember unresolved classes */
        unresolved.add(a);
/** END Custom change: remember unresolved classes */
        return null;
      }
    }
  }

  private boolean slowIsSubclass(IClass sub, IClass sup) {
    if (sub == sup) {
      return true;
    } else {
      IClass parent = sub.getSuperclass();
      if (parent == null) {
        return false;
      } else {
        return slowIsSubclass(parent, sup);
      }
    }
  }

  /**
   * Is c a subclass of T?
   * 
   * @throws IllegalArgumentException if c is null
   */
  public boolean isSubclassOf(IClass c, IClass t) {
    if (c == null) {
      throw new IllegalArgumentException("c is null");
    }
    assert t != null : "null T";

    if (c.isArrayClass()) {
      if (t.getReference() == TypeReference.JavaLangObject) {
        return true;
      } else if (t.getReference().isArrayType()) {
        TypeReference elementType = t.getReference().getArrayElementType();
        if (elementType.isPrimitiveType()) {
          return elementType.equals(c.getReference().getArrayElementType());
        } else {
          IClass elementKlass = lookupClass(elementType);
          if (elementKlass == null) {
            // uh oh.
            Warnings.add(ClassHierarchyWarning.create("could not find " + elementType));
            return false;
          }
          IClass ce = ((ArrayClass) c).getElementClass();
          if (ce == null) {
            return false;
          }
          if (elementKlass.isInterface()) {
            return implementsInterface(ce, elementKlass);
          } else {
            return isSubclassOf(ce, elementKlass);
          }
        }
      } else {
        return false;
      }
    } else {
      if (t.getReference().isArrayType()) {
        return false;
      }
      if (c.getReference().equals(t.getReference())) {
        return true;
      }
      Node n1 = map.get(c.getReference());
      if (n1 == null) {
        // some wacky case, like a FakeRootClass
        return false;
      }
      Node n2 = map.get(t.getReference());
      if (n2 == null) {
        // some wacky case, like a FakeRootClass
        return false;
      }
      if (n1.left == -1) {
        return slowIsSubclass(c, t);
      } else if (n2.left == -1) {
        return slowIsSubclass(c, t);
      } else {
        return (n2.left <= n1.left) && (n1.left <= n2.right);
      }
    }
  }

  /**
   * Does c implement i?
   * 
   * @return true iff i is an interface and c is a class that implements i, or c is an interface that extends i.
   */
  public boolean implementsInterface(IClass c, IClass i) {
      throw new IllegalArgumentException("Cannot ask implementsInterface with i == null");
    }
    if (c == null) {
      throw new IllegalArgumentException("Cannot ask implementsInterface with c == null");
    }
    if (!i.isInterface()) {
      return false;
    }
    if (c.equals(i)) {
      return true;
    }
    if (c.isArrayClass()) {
      // arrays implement Cloneable and Serializable
      return i.equals(lookupClass(TypeReference.JavaLangCloneable)) || i.equals(lookupClass(TypeReference.JavaIoSerializable));
    }
    Set impls = implementors.get(i);
    if (impls != null && impls.contains(c)) {
      return true;
    }
    return false;
  }

  /**
   * Return set of all subclasses of type in the Class Hierarchy TODO: Tune this implementation. Consider caching if necessary.
   */
  public Collection computeSubClasses(TypeReference type) {
    IClass t = lookupClass(type);
    if (t == null) {
      throw new IllegalArgumentException("could not find class for TypeReference " + type);
    }
    // a hack: TODO: work on better caching
    if (t.getReference().equals(TypeReference.JavaLangError)) {
      if (subclassesOfError == null) {
        subclassesOfError = computeSubClassesInternal(t);
      }
      return subclassesOfError;
    } else if (t.getReference().equals(TypeReference.JavaLangRuntimeException)) {
      if (runtimeExceptionClasses == null) {
        runtimeExceptionClasses = computeSubClassesInternal(t);
      }
      return runtimeExceptionClasses;
    } else {
      return computeSubClassesInternal(t);
    }
  }

  /**
   * Solely for optimization; return a Collection representing the subclasses of Error
   * 
   * kind of ugly. a better scheme?
   */
  public Collection getJavaLangErrorTypes() {
    if (subTypeRefsOfError == null) {
      computeSubClasses(TypeReference.JavaLangError);
      subTypeRefsOfError = HashSetFactory.make(subclassesOfError.size());
      for (Iterator it = subclassesOfError.iterator(); it.hasNext();) {
        IClass klass = (IClass) it.next();
        subTypeRefsOfError.add(klass.getReference());
      }
    }
    return Collections.unmodifiableCollection(subTypeRefsOfError);
  }

  /**
   * Solely for optimization; return a Collection representing the subclasses of RuntimeException
   * 
   * kind of ugly. a better scheme?
   */
  public Collection getJavaLangRuntimeExceptionTypes() {
    if (runtimeExceptionTypeRefs == null) {
      computeSubClasses(TypeReference.JavaLangRuntimeException);
      runtimeExceptionTypeRefs = HashSetFactory.make(runtimeExceptionClasses.size());
      for (Iterator it = runtimeExceptionClasses.iterator(); it.hasNext();) {
        IClass klass = (IClass) it.next();
        runtimeExceptionTypeRefs.add(klass.getReference());
      }
    }
    return Collections.unmodifiableCollection(runtimeExceptionTypeRefs);
  }

  /**
  private Collection getImmediateArraySubclasses(ArrayClass klass) {
   * Return set of all subclasses of type in the Class Hierarchy TODO: Tune this implementation. Consider caching if necessary.
   * 
   * @return Set of IClasses
   */
  private Set computeSubClassesInternal(IClass T) {
    if (T.isArrayClass()) {
      return Collections.singleton(T);
    }
    Node node = findNode(T);
    assert node != null : "null node for class " + T;
    HashSet result = HashSetFactory.make(3);
    result.add(T);
    for (Iterator it = node.getChildren(); it.hasNext();) {
      Node child = it.next();
  }
      result.addAll(computeSubClasses(child.klass.getReference()));
    }
    return result;
  }

  public boolean isInterface(TypeReference type) {
    IClass T = lookupClass(type);
    assert T != null : "Null lookup for " + type;
    return T.isInterface();
  }

  /**
   * TODO: tune this if necessary
   * 
   * @param type an interface
   * @return Set of IClass that represent implementors of the interface
   */
  public Set getImplementors(TypeReference type) {
    IClass T = lookupClass(type);
    Set result = implementors.get(T);
    if (result == null) {
      return Collections.emptySet();
    }
    return Collections.unmodifiableSet(result);
  }

  public Iterator iterator() {
    Function toClass = new Function() {
      public IClass apply(Node n) {
        return n.klass;
      }
    };
    return new MapIterator(map.values().iterator(), toClass);
  }

  /**
   * @return The number of classes present in the class hierarchy.
   */
  public int getNumberOfClasses() {
    return map.keySet().size();
  }

  public IClassLoader[] getLoaders() {
    return loaders;
  }

  public IClassLoader getLoader(ClassLoaderReference loaderRef) {
    for (int i = 0; i < loaders.length; i++) {
      if (loaders[i].getReference().equals(loaderRef)) {
        return loaders[i];
      }
    }
    Assertions.UNREACHABLE();
    return null;
  }

  public AnalysisScope getScope() {
    return scope;
  }

  /**
   * @return the number of classes that immediately extend klass. if klass is an array class A[][]...[], we return number of
   *         immediate subclasses of A. If A is primitive, we return 0.
   */
  public int getNumberOfImmediateSubclasses(IClass klass) {
    if (klass.isArrayClass()) {
      IClass innermost = getInnermostTypeOfArrayClass(klass);
      return innermost == null ? 0 : getNumberOfImmediateSubclasses(innermost);
    }
    Node node = findNode(klass);
    return node.children.size();
  }

  /**
   * @param klass
   * @return the classes that immediately extend klass. if klass is an array class A[][]...[], we return array classes B[][]...[]
   *         (same dimensionality) where B is an immediate subclass of A. If A is primitive, we return the empty set.
   */
  public Collection getImmediateSubclasses(IClass klass) {
    if (klass.isArrayClass()) {
      return getImmediateArraySubclasses((ArrayClass)klass);
    }
    Function node2Class = new Function() {
      public IClass apply(Node n) {
        return n.klass;
      }
    };
    return Iterator2Collection.toSet(new MapIterator(findNode(klass).children.iterator(), node2Class));
  }
    IClass innermost = getInnermostTypeOfArrayClass(klass);
    if (innermost == null) {
      return Collections.emptySet();
    }
    Collection innermostSubclasses = getImmediateSubclasses(innermost);
    int dim = klass.getDimensionality();
    Collection result = HashSetFactory.make();
    for (IClass k : innermostSubclasses) {
      TypeReference ref = k.getReference();
      for (int i = 0; i < dim; i++) {
        ref = ref.getArrayTypeForElementType();
      }
      result.add(lookupClass(ref));
    }

  /**
   * for an array class, get the innermost type, or null if it's primitive
   */
  private IClass getInnermostTypeOfArrayClass(IClass klass) {
    TypeReference result = klass.getReference();
    while (result.isArrayType()) {
      result = result.getArrayElementType();
    }
    return result.isPrimitiveType() ? null : lookupClass(result);
  }

  /**
   * @return a ClassHierarchy object representing the analysis scope
   * @throws ClassHierarchyException
   */
  public static ClassHierarchy make(AnalysisScope scope) throws ClassHierarchyException {
    if (scope == null) {
      throw new IllegalArgumentException("null scope");
    }
    return make(scope, new ClassLoaderFactoryImpl(scope.getExclusions()));
  }

  /**
   * temporarily marking this internal to avoid infinite sleep with randomly chosen IProgressMonitor.
   */
  public static ClassHierarchy make(AnalysisScope scope, IProgressMonitor monitor) throws ClassHierarchyException {
    if (scope == null) {
      throw new IllegalArgumentException("null scope");
    }
    return make(scope, new ClassLoaderFactoryImpl(scope.getExclusions()), monitor);
  }

  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory) throws ClassHierarchyException {
    if (scope == null) {
      throw new IllegalArgumentException("null scope");
    }
    if (factory == null) {
      throw new IllegalArgumentException("null factory");
    }
    return new ClassHierarchy(scope, factory, null);
  }

  /**
   * temporarily marking this internal to avoid infinite sleep with randomly chosen IProgressMonitor.
   */
  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, IProgressMonitor monitor)
      throws ClassHierarchyException {
    return new ClassHierarchy(scope, factory, monitor);
  }

  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, Set languages)
      throws ClassHierarchyException {
    return new ClassHierarchy(scope, factory, languages, null);
  }

  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, Language language)
      throws ClassHierarchyException {
    return new ClassHierarchy(scope, factory, language, null);
  }

  /**
   * temporarily marking this internal to avoid infinite sleep with randomly chosen IProgressMonitor. TODO: nanny for testgen
   */
  public static ClassHierarchy make(AnalysisScope scope, ClassLoaderFactory factory, Language language, IProgressMonitor monitor)
      throws ClassHierarchyException {
    if (factory == null) {
      throw new IllegalArgumentException("null factory");
    }
    return new ClassHierarchy(scope, factory, language, monitor);
  }

  public IClass getRootClass() {
    return root.getJavaClass();
  }

  public boolean isRootClass(IClass c) throws IllegalArgumentException {
    if (c == null) {
      throw new IllegalArgumentException("c == null");
    }
    return c.equals(root.getJavaClass());
  }

  public int getNumber(IClass c) {
    return map.get(c.getReference()).left;
  }

  /**
   * A warning for when we fail to resolve the type for a checkcast
   */
  private static class ClassExclusion extends Warning {

    final TypeReference klass;

    final String message;

    ClassExclusion(TypeReference klass, String message) {
      super(Warning.MODERATE);
      this.klass = klass;
      this.message = message;
    }

    @Override
    public String getMsg() {
      return getClass().toString() + " : " + klass + " " + message;
    }

    public static ClassExclusion create(TypeReference klass, String message) {
      return new ClassExclusion(klass, message);
    }
  }

  /**
   * Does an expression c1 x := c2 y typecheck?
   * 
   * i.e. is c2 a subtype of c1?
   * 
   * @throws IllegalArgumentException if c1 is null
   * @throws IllegalArgumentException if c2 is null
   */
  public boolean isAssignableFrom(IClass c1, IClass c2) {
    if (c2 == null) {
      throw new IllegalArgumentException("c2 is null");
    }
    if (c1 == null) {
      throw new IllegalArgumentException("c1 is null");
    }
    if (c1.isInterface()) {
      return implementsInterface(c2, c1);
    } else {
      if (c2.isInterface()) {
        return c1.equals(getRootClass());
      } else {
        return isSubclassOf(c2, c1);
      }
    }
  }

/** BEGIN Custom change: remember unresolved classes */
  private final Set unresolved = HashSetFactory.make();

  public final Set getUnresolvedClasses() {
    return unresolved;
  }

/** END Custom change: remember unresolved classes */
}
File
ClassHierarchy.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
import com.ibm.wala.ssa.DefUse;
   */
<<<<<<< HEAD
  /**
/*******************************************************************************
 * Copyright (c) 2006 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.ipa.slicer;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.cdg.ControlDependenceGraph;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.impl.SetOfClasses;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
import com.ibm.wala.ipa.cfg.ExceptionPrunedCFG;
import com.ibm.wala.ipa.modref.DelegatingExtendedHeapModel;
import com.ibm.wala.ipa.modref.ExtendedHeapModel;
import com.ibm.wala.ipa.modref.ModRef;
import com.ibm.wala.ipa.slicer.Slicer.ControlDependenceOptions;
import com.ibm.wala.ipa.slicer.Slicer.DataDependenceOptions;
import com.ibm.wala.ipa.slicer.Statement.Kind;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAAbstractThrowInstruction;
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
import com.ibm.wala.ssa.SSAArrayReferenceInstruction;
import com.ibm.wala.ssa.SSACFG;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAFieldAccessInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAInstanceofInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.Filter;
import com.ibm.wala.util.collections.FilterIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.Iterator2Iterable;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.graph.NumberedGraph;
import com.ibm.wala.util.graph.dominators.Dominators;
import com.ibm.wala.util.graph.labeled.SlowSparseNumberedLabeledGraph;
import com.ibm.wala.util.intset.BitVectorIntSet;
import com.ibm.wala.util.intset.IntIterator;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.OrdinalSet;

/**
 * Program dependence graph for a single call graph node
 */
public class PDG implements NumberedGraph {

/** BEGIN Custom change: control deps */                
  public enum Dependency {CONTROL_DEP, DATA_AND_CONTROL_DEP};
  
  private final SlowSparseNumberedLabeledGraph delegate =
    new SlowSparseNumberedLabeledGraph(Dependency.DATA_AND_CONTROL_DEP);
/** END Custom change: control deps */                

  private final static boolean VERBOSE = false;

  private final CGNode node;

  private Statement[] paramCalleeStatements;

  private Statement[] returnStatements;
   * TODO: using CallSiteReference is sloppy. clean it up.
   */

  private final Map callSite2Statement = HashMapFactory.make();

  private final Map> callerParamStatements = HashMapFactory.make();

  private final Map> callerReturnStatements = HashMapFactory.make();

  private final HeapExclusions exclusions;

  private final Collection locationsHandled = HashSetFactory.make();

  private final PointerAnalysis pa;

  private final ExtendedHeapModel heapModel;

  private final Map> mod;

  private final DataDependenceOptions dOptions;

  private final ControlDependenceOptions cOptions;

  private final CallGraph cg;

  private final ModRef modRef;

  private final Map> ref;

  private final boolean ignoreAllocHeapDefs;

  private boolean isPopulated = false;

  /**
   * @param mod the set of heap locations which may be written (transitively) by this node. These are logically return values in the
   *          SDG.
   * @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG.
   * @throws IllegalArgumentException if node is null
   */
   * Create all control dependence edges in this PDG.
  public PDG(final CGNode node, PointerAnalysis pa, Map> mod,
      Map> ref, DataDependenceOptions dOptions, ControlDependenceOptions cOptions,
      HeapExclusions exclusions, CallGraph cg, ModRef modRef) {
    this(node, pa, mod, ref, dOptions, cOptions, exclusions, cg, modRef, false);
  }

  /**
   * @param mod the set of heap locations which may be written (transitively) by this node. These are logically return values in the
   *          SDG.
   * @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG.
   * @throws IllegalArgumentException if node is null
   */
  public PDG(final CGNode node, PointerAnalysis pa, Map> mod,
      Map> ref, DataDependenceOptions dOptions, ControlDependenceOptions cOptions,
      HeapExclusions exclusions, CallGraph cg, ModRef modRef, boolean ignoreAllocHeapDefs) {

    super();
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    this.cg = cg;
    this.node = node;
    this.heapModel = pa == null ? null : new DelegatingExtendedHeapModel(pa.getHeapModel());
    this.pa = pa;
    this.dOptions = dOptions;
    this.cOptions = cOptions;
    this.mod = mod;
    this.exclusions = exclusions;
    this.modRef = modRef;
    this.ref = ref;
    this.ignoreAllocHeapDefs = ignoreAllocHeapDefs;
  }

  /**
   * WARNING: Since we're using a {@link HashMap} of {@link SSAInstruction}s, and equals() of {@link SSAInstruction} assumes a
   * canonical representative for each instruction, we must ensure that we use the same IR object throughout
   * initialization!!
   */
  private void populate() {
    if (!isPopulated) {
      // ensure that we keep the single, canonical IR live throughout initialization, while the instructionIndices map
      // is live.
      IR ir = node.getIR();
      isPopulated = true;

      Map instructionIndices = computeInstructionIndices(ir);
      createNodes(ref, cOptions, ir);
      createScalarEdges(cOptions, ir, instructionIndices);
    }
  }

  private void createScalarEdges(ControlDependenceOptions cOptions, IR ir, Map instructionIndices) {
          // {
    createScalarDataDependenceEdges(ir, instructionIndices);
    createControlDependenceEdges(cOptions, ir, instructionIndices);
  }

  /**
   * return the set of all PARAM_CALLER and HEAP_PARAM_CALLER statements associated with a given call
   */
  public Set getCallerParamStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException {
    if (call == null) {
      throw new IllegalArgumentException("call == null");
    }
    populate();
    return callerParamStatements.get(call.getCallSite());
  }

  /**
   * return the set of all PARAM_CALLER, HEAP_PARAM_CALLER, and NORMAL statements (i.e., the actual call statement) associated with
   * a given call
   */
  public Set getCallStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException {
    Set callerParamStatements = getCallerParamStatements(call);
    Set result = HashSetFactory.make(callerParamStatements.size() + 1);
    result.addAll(callerParamStatements);
    result.add(callSite2Statement.get(call.getCallSite()));
    return result;
  }

  /**
   * return the set of all NORMAL_RETURN_CALLER and HEAP_RETURN_CALLER statements associated with a given call.
   */
  public Set getCallerReturnStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException {
    if (call == null) {
      throw new IllegalArgumentException("call == null");
    }
    populate();
    return callerReturnStatements.get(call.getCallSite());
  }

  /**
  private void createControlDependenceEdges(ControlDependenceOptions cOptions, IR ir,
      Map instructionIndices) {
    if (cOptions.equals(ControlDependenceOptions.NONE)) {
      return;
    }
    if (ir == null) {
      return;
    }
    ControlFlowGraph controlFlowGraph = ir.getControlFlowGraph();
    if (cOptions.equals(ControlDependenceOptions.NO_EXCEPTIONAL_EDGES)) {
      controlFlowGraph = ExceptionPrunedCFG.make(controlFlowGraph);
      // In case the CFG has no nodes left because the only control dependencies
      // were
      // exceptional, simply return because at this point there are no nodes.
      // Otherwise, later this may raise an Exception.
      if (controlFlowGraph.getNumberOfNodes() == 0) {
        return;
      }
    } else {
      Assertions.productionAssertion(cOptions.equals(ControlDependenceOptions.FULL));
    }

    ControlDependenceGraph cdg = new ControlDependenceGraph(
        controlFlowGraph);
    for (ISSABasicBlock bb : cdg) {
      if (bb.isExitBlock()) {
        // nothing should be control-dependent on the exit block.
        continue;
      }

      Statement src = null;
      if (bb.isEntryBlock()) {
        src = new MethodEntryStatement(node);
      } else {
        SSAInstruction s = ir.getInstructions()[bb.getLastInstructionIndex()];
        if (s == null) {
          // should have no control dependent successors.
          // leave src null.
        } else {
          src = ssaInstruction2Statement(s, ir, instructionIndices);
          // add edges from call statements to parameter passing and return
          // SJF: Alexey and I think that we should just define ParamStatements
          // as
          // being control dependent on nothing ... they only represent pure
          // data dependence. So, I'm commenting out the following.
          // if (s instanceof SSAAbstractInvokeInstruction) {
          // SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction)
          // s;
          // for (Statement st : callerParamStatements.get(call.getCallSite()))
          // {
          // addEdge(src, st);
          // }
          // for (Statement st : callerReturnStatements.get(call.getCallSite()))
          // addEdge(src, st);
          // }
          // }
        }
      }
      // add edges for every control-dependent statement in the IR, if there are
      // any
      // control-dependent successors
      if (src != null) {
        for (Iterator succ = cdg.getSuccNodes(bb); succ.hasNext();) {
          ISSABasicBlock bb2 = succ.next();
          for (Iterator it2 = bb2.iterator(); it2.hasNext();) {
            SSAInstruction st = it2.next();
            if (st != null) {
              Statement dest = ssaInstruction2Statement(st, ir, instructionIndices);
              assert src != null;
              delegate.addEdge(src, dest);
/** BEGIN Custom change: control deps */                
              delegate.addEdge(src, dest, Dependency.CONTROL_DEP);
/** END Custom change: control deps */                
            }
          }
        }
      }
    }

    // the CDG does not represent control dependences from the entry node.
    // add these manually
    // We add control dependences to all instructions in all basic blocks B that _must_ execute.
    // B is the set of blocks that dominate the exit basic block
    Statement methodEntry = new MethodEntryStatement(node);
    Dominators dom = Dominators.make(controlFlowGraph, controlFlowGraph.entry());
    for (ISSABasicBlock exitDom : Iterator2Iterable.make(dom.dominators(controlFlowGraph.exit()))) {
      for (SSAInstruction st : exitDom) {
        Statement dest = ssaInstruction2Statement(st, ir, instructionIndices);
        delegate.addEdge(methodEntry, dest);
/** BEGIN Custom change: control deps */                
        delegate.addEdge(methodEntry, dest, Dependency.CONTROL_DEP);
/** END Custom change: control deps */                
      }
    }
    // add CD from method entry to all callee parameter assignments
    // SJF: Alexey and I think that we should just define ParamStatements as
    // being control dependent on nothing ... they only represent pure
    // data dependence. So, I'm commenting out the following.
    // for (int i = 0; i < paramCalleeStatements.length; i++) {
    // addEdge(methodEntry, paramCalleeStatements[i]);
    // }

    /**
     * JTD: While phi nodes live in a particular basic block, they represent a meet of values from multiple blocks. Hence, they are
     * really like multiple statements that are control dependent in the manner of the predecessor blocks. When the slicer is
     * following both data and control dependences, it therefore seems right to add control dependence edges to represent how a phi
     * node depends on predecessor blocks.
     */
    if (!dOptions.equals(DataDependenceOptions.NONE)) {
      for (ISSABasicBlock bb : cdg) {
        for (Iterator ps = bb.iteratePhis(); ps.hasNext();) {
          SSAPhiInstruction phi = ps.next();
          Statement phiSt = ssaInstruction2Statement(phi, ir, instructionIndices);
          int phiUseIndex = 0;
          for (Iterator preds = controlFlowGraph.getPredNodes(bb); preds.hasNext();) {
            ISSABasicBlock pb = preds.next();
            int use = phi.getUse(phiUseIndex);
            if (use == AbstractIntStackMachine.TOP) {
              // the predecessor is part of some infeasible bytecode. we probably don't want slices to include such code, so ignore.
              continue;
            }
            if (controlFlowGraph.getSuccNodeCount(pb) > 1) {
              // in this case, there is more than one edge from the
              // predecessor block, hence the phi node actually
              // depends on the last instruction in the previous
              // block, rather than having the same dependences as
              // statements in that block.
              SSAInstruction pss = ir.getInstructions()[pb.getLastInstructionIndex()];
              assert pss != null;
              Statement pst = ssaInstruction2Statement(pss, ir, instructionIndices);
              delegate.addEdge(pst, phiSt);
/** BEGIN Custom change: control deps */                
              delegate.addEdge(pst, phiSt, Dependency.CONTROL_DEP);
/** END Custom change: control deps */                
            } else {
              for (Iterator cdps = cdg.getPredNodes(pb); cdps.hasNext();) {
                ISSABasicBlock cpb = cdps.next();
/** BEGIN Custom change: control deps */                
                if (cpb.getLastInstructionIndex() < 0) {
                  continue;
                }
/** END Custom change: control deps */                
                SSAInstruction cps = ir.getInstructions()[cpb.getLastInstructionIndex()];
                assert cps != null : "unexpected null final instruction for CDG predecessor " + cpb + " in node " + node;
                Statement cpst = ssaInstruction2Statement(cps, ir, instructionIndices);
                delegate.addEdge(cpst, phiSt);
/** BEGIN Custom change: control deps */                
                delegate.addEdge(cpst, phiSt, Dependency.CONTROL_DEP);
/** END Custom change: control deps */                
              }
            }
            phiUseIndex++;
          }
        }
      }
    }
  }

  /**
   * Create all data dependence edges in this PDG.
   * 
   * Scalar dependences are taken from SSA def-use information.
   * 
   * Heap dependences are computed by a reaching defs analysis.
   * 
   * @param pa
   * @param mod
   */
  private void createScalarDataDependenceEdges(IR ir, Map instructionIndices) {
    if (dOptions.equals(DataDependenceOptions.NONE)) {
      return;
    }

    if (ir == null) {
      return;
    }

    // this is tricky .. I'm explicitly creating a new DefUse to make sure it refers to the instructions we need from
    // the "one true" ir of the moment.
    DefUse DU = new DefUse(ir);
    SSAInstruction[] instructions = ir.getInstructions();

    //
    // TODO: teach some other bit of code about the uses of
    // GetCaughtException, and then delete this code.
    //
    if (!dOptions.isIgnoreExceptions()) {
      for (ISSABasicBlock bb : ir.getControlFlowGraph()) {
        if (bb.isCatchBlock()) {
          SSACFG.ExceptionHandlerBasicBlock ehbb = (SSACFG.ExceptionHandlerBasicBlock) bb;

          if (ehbb.getCatchInstruction() != null) {
            Statement c = ssaInstruction2Statement(ehbb.getCatchInstruction(), ir, instructionIndices);

            for (ISSABasicBlock pb : ir.getControlFlowGraph().getExceptionalPredecessors(ehbb)) {
              SSAInstruction st = instructions[pb.getLastInstructionIndex()];

              if (st instanceof SSAAbstractInvokeInstruction) {
                delegate.addEdge(new ExceptionalReturnCaller(node, pb.getLastInstructionIndex()), c);
              } else if (st instanceof SSAAbstractThrowInstruction) {
                delegate.addEdge(ssaInstruction2Statement(st, ir, instructionIndices), c);
              }
            }
          }
        }
      }
    }

    for (Iterator it = iterator(); it.hasNext();) {
      Statement s = it.next();
      switch (s.getKind()) {
      case NORMAL:
      case CATCH:
      case PI:
      case PHI: {
        SSAInstruction statement = statement2SSAInstruction(instructions, s);
        // note that data dependencies from invoke instructions will pass
        // interprocedurally
        if (!(statement instanceof SSAAbstractInvokeInstruction)) {
          if (dOptions.isTerminateAtCast() && (statement instanceof SSACheckCastInstruction)) {
            break;
          }
          if (dOptions.isTerminateAtCast() && (statement instanceof SSAInstanceofInstruction)) {
            break;
          }
          // add edges from this statement to every use of the def of this
          // statement
          for (int i = 0; i < statement.getNumberOfDefs(); i++) {
            int def = statement.getDef(i);
            for (Iterator it2 = DU.getUses(def); it2.hasNext();) {
              SSAInstruction use = it2.next();
              if (dOptions.isIgnoreBasePtrs()) {
                if (use instanceof SSANewInstruction) {
                  // cut out array length parameters
                  continue;
                }
                if (hasBasePointer(use)) {
                  int base = getBasePointer(use);
                  if (def == base) {
                    // skip the edge to the base pointer
                    continue;
                  }
                  if (use instanceof SSAArrayReferenceInstruction) {
                    SSAArrayReferenceInstruction arr = (SSAArrayReferenceInstruction) use;
                    if (def == arr.getIndex()) {
                      // skip the edge to the array index
                      continue;
                    }
                  }
                }
              }
              Statement u = ssaInstruction2Statement(use, ir, instructionIndices);
              delegate.addEdge(s, u);
            }
          }
        }
        break;
      }
      case EXC_RET_CALLER:
      case NORMAL_RET_CALLER:
      case PARAM_CALLEE: {
        if (dOptions.isIgnoreExceptions()) {
          assert !s.getKind().equals(Kind.EXC_RET_CALLER);
        }

        ValueNumberCarrier a = (ValueNumberCarrier) s;
        Assertions.UNREACHABLE(s.toString());
        for (Iterator it2 = DU.getUses(a.getValueNumber()); it2.hasNext();) {
          SSAInstruction use = it2.next();
          if (dOptions.isIgnoreBasePtrs()) {
            if (use instanceof SSANewInstruction) {
              // cut out array length parameters
              continue;
            }
            if (hasBasePointer(use)) {
              int base = getBasePointer(use);
              if (a.getValueNumber() == base) {
                // skip the edge to the base pointer
                continue;
              }
              if (use instanceof SSAArrayReferenceInstruction) {
                SSAArrayReferenceInstruction arr = (SSAArrayReferenceInstruction) use;
                if (a.getValueNumber() == arr.getIndex()) {
                  // skip the edge to the array index
                  continue;
                }
              }
            }
          }
          Statement u = ssaInstruction2Statement(use, ir, instructionIndices);
          delegate.addEdge(s, u);
        }
        break;
      }
      case NORMAL_RET_CALLEE:
        for (NormalStatement ret : computeReturnStatements(ir)) {
          delegate.addEdge(ret, s);
        }
        break;
      case EXC_RET_CALLEE:
        if (dOptions.isIgnoreExceptions()) {
          Assertions.UNREACHABLE();
        }
        // TODO: this is overly conservative. deal with catch blocks?
        for (IntIterator ii = getPEIs(ir).intIterator(); ii.hasNext();) {
          int index = ii.next();
          SSAInstruction pei = ir.getInstructions()[index];
          if (dOptions.isTerminateAtCast() && (pei instanceof SSACheckCastInstruction)) {
            continue;
          }
          if (pei instanceof SSAAbstractInvokeInstruction) {
            Statement st = new ExceptionalReturnCaller(node, index);
            delegate.addEdge(st, s);
          } else {
            delegate.addEdge(new NormalStatement(node, index), s);
          }
        }
        break;
      case PARAM_CALLER: {
        ParamCaller pac = (ParamCaller) s;
        int vn = pac.getValueNumber();
        // note that if the caller is the fake root method and the parameter
        // type is primitive,
    case NORMAL:
        // it's possible to have a value number of -1. If so, just ignore it.
        if (vn > -1) {
          if (ir.getSymbolTable().isParameter(vn)) {
            Statement a = new ParamCallee(node, vn);
            delegate.addEdge(a, pac);
          } else {
            SSAInstruction d = DU.getDef(vn);
            if (dOptions.isTerminateAtCast() && (d instanceof SSACheckCastInstruction)) {
              break;
            }
            if (d != null) {
              if (d instanceof SSAAbstractInvokeInstruction) {
                SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction) d;
                if (vn == call.getException()) {
                  Statement st = new ExceptionalReturnCaller(node, instructionIndices.get(d));
                  delegate.addEdge(st, pac);
                } else {
                  Statement st = new NormalReturnCaller(node, instructionIndices.get(d));
                  delegate.addEdge(st, pac);
                }
              } else {
                Statement ds = ssaInstruction2Statement(d, ir, instructionIndices);
                delegate.addEdge(ds, pac);
              }
            }
          }
        }
      }
        break;

      case HEAP_RET_CALLEE:
      case HEAP_RET_CALLER:
      case HEAP_PARAM_CALLER:
      case HEAP_PARAM_CALLEE:
      case METHOD_ENTRY:
      case METHOD_EXIT:
        // do nothing
        break;
      default:
        break;
      }
    }
  }

  private static class SingletonSet extends SetOfClasses implements Serializable {

    /* Serial version */
    private static final long serialVersionUID = -3256390509887654324L;

    private final TypeReference t;

    SingletonSet(TypeReference t) {
      this.t = t;
    }

    @Override
    public void add(IClass klass) {
      Assertions.UNREACHABLE();
    }

    @Override
    public boolean contains(String klassName) {
      Assertions.UNREACHABLE();
      return false;
    }

    @Override
    public boolean contains(TypeReference klass) {
      return t.equals(klass);
    }
  }

  private static class SetComplement extends SetOfClasses implements Serializable {

    /* Serial version */
    private static final long serialVersionUID = -3256390509887654323L;

    private final SetOfClasses set;

    SetComplement(SetOfClasses set) {
      this.set = set;
    }

    static SetComplement complement(SetOfClasses set) {
      return new SetComplement(set);
    }

    @Override
    public void add(IClass klass) {
      Assertions.UNREACHABLE();
    }

    @Override
    public boolean contains(String klassName) {
      Assertions.UNREACHABLE();
      return false;
    }

    @Override
    public boolean contains(TypeReference klass) {
      return !set.contains(klass);
    }
  }

  /**
   * Create heap data dependence edges in this PDG relevant to a particular {@link PointerKey}.
   */
  private void createHeapDataDependenceEdges(final PointerKey pk) {

    if (locationsHandled.contains(pk)) {
      return;
    } else {
      locationsHandled.add(pk);
    }
    if (dOptions.isIgnoreHeap() || (exclusions != null && exclusions.excludes(pk))) {
      return;
    }

    TypeReference t = HeapExclusions.getType(pk);
    if (t == null) {
      return;
    }

    // It's OK to create a new IR here; we're not keeping any hashing live up to this point
    IR ir = node.getIR();
    if (ir == null) {
      return;
    }

    if (VERBOSE) {
      System.err.println("Location " + pk);
    }

    // in reaching defs calculation, exclude heap statements that are
    // irrelevant.
    Filter f = new Filter() {
      public boolean accepts(Object o) {
        if (o instanceof HeapStatement) {
          HeapStatement h = (HeapStatement) o;
          return h.getLocation().equals(pk);
        } else {
          return true;
        }
      }
    };
    Collection relevantStatements = Iterator2Collection.toSet(new FilterIterator(iterator(), f));

    Map> heapReachingDefs = new HeapReachingDefs(modRef).computeReachingDefs(node, ir, pa, mod,
        relevantStatements, new HeapExclusions(SetComplement.complement(new SingletonSet(t))), cg);

    for (Statement st : heapReachingDefs.keySet()) {
      switch (st.getKind()) {
      case NORMAL:
      case CATCH:
      case PHI:
      case PI: {
        OrdinalSet defs = heapReachingDefs.get(st);
        if (defs != null) {
          for (Statement def : defs) {
            delegate.addEdge(def, st);
          }
        }
      }
        break;
      case EXC_RET_CALLER:
      case NORMAL_RET_CALLER:
      case PARAM_CALLEE:
      case NORMAL_RET_CALLEE:
      case PARAM_CALLER:
      case EXC_RET_CALLEE:
        break;
      case HEAP_RET_CALLEE:
      case HEAP_RET_CALLER:
      case HEAP_PARAM_CALLER: {
        OrdinalSet defs = heapReachingDefs.get(st);
        if (defs != null) {
          for (Statement def : defs) {
            delegate.addEdge(def, st);
          }
        }
        break;
      }
      case HEAP_PARAM_CALLEE:
      case METHOD_ENTRY:
      case METHOD_EXIT:
        // do nothing .. there are no incoming edges
        break;
      default:
        Assertions.UNREACHABLE(st.toString());
        break;
      }
    }
  }

  private boolean hasBasePointer(SSAInstruction use) {
    if (use instanceof SSAFieldAccessInstruction) {
      SSAFieldAccessInstruction f = (SSAFieldAccessInstruction) use;
      return !f.isStatic();
    } else if (use instanceof SSAArrayReferenceInstruction) {
      return true;
    } else if (use instanceof SSAArrayLengthInstruction) {
      return true;
    } else {
      return false;
    }
  }

  private int getBasePointer(SSAInstruction use) {
    if (use instanceof SSAFieldAccessInstruction) {
      SSAFieldAccessInstruction f = (SSAFieldAccessInstruction) use;
      return f.getRef();
    } else if (use instanceof SSAArrayReferenceInstruction) {
      SSAArrayReferenceInstruction a = (SSAArrayReferenceInstruction) use;
      return a.getArrayRef();

    } else if (use instanceof SSAArrayLengthInstruction) {
      SSAArrayLengthInstruction s = (SSAArrayLengthInstruction) use;
      return s.getArrayRef();
    } else {
      Assertions.UNREACHABLE("BOOM");
      return -1;
    }
  }

  /**
   * @return Statements representing each return instruction in the ir
   */
  private Collection computeReturnStatements(final IR ir) {
    Filter filter = new Filter() {
      public boolean accepts(Object o) {
        if (o instanceof NormalStatement) {
          NormalStatement s = (NormalStatement) o;
          SSAInstruction st = ir.getInstructions()[s.getInstructionIndex()];
          return st instanceof SSAReturnInstruction;
        } else {
          return false;
        }
      }
    };
    return Iterator2Collection.toSet(new FilterIterator(iterator(), filter));
  }

  /**
   * @return {@link IntSet} representing instruction indices of each PEI in the ir
   */
  private IntSet getPEIs(final IR ir) {
    BitVectorIntSet result = new BitVectorIntSet();
    for (int i = 0; i < ir.getInstructions().length; i++) {
      if (ir.getInstructions()[i] != null && ir.getInstructions()[i].isPEI()) {
        result.add(i);
      }
    }
    return result;
  }

  /**
   * Wrap an {@link SSAInstruction} in a {@link Statement}. WARNING: Since we're using a {@link HashMap} of {@link SSAInstruction}s,
   * and equals() of {@link SSAInstruction} assumes a canonical representative for each instruction, we must ensure that we
   * use the same IR object throughout initialization!!
   */
  private Statement ssaInstruction2Statement(SSAInstruction s, IR ir, Map instructionIndices) {
    return ssaInstruction2Statement(node, s, instructionIndices, ir);
  }

  public static synchronized Statement ssaInstruction2Statement(CGNode node, SSAInstruction s,
      Map instructionIndices, IR ir) {
    if (node == null) {
      throw new IllegalArgumentException("null node");
    }
    if (s == null) {
      throw new IllegalArgumentException("null s");
    }
    if (s instanceof SSAPhiInstruction) {
      SSAPhiInstruction phi = (SSAPhiInstruction) s;
      return new PhiStatement(node, phi);
    } else if (s instanceof SSAPiInstruction) {
      SSAPiInstruction pi = (SSAPiInstruction) s;
      return new PiStatement(node, pi);
    } else if (s instanceof SSAGetCaughtExceptionInstruction) {
      return new GetCaughtExceptionStatement(node, ((SSAGetCaughtExceptionInstruction) s));
    } else {
      Integer x = instructionIndices.get(s);
      if (x == null) {
        Assertions.UNREACHABLE(s.toString() + "\nnot found in map of\n" + ir);
      }
      return new NormalStatement(node, x.intValue());
    }
  }

  /**
   * @return for each SSAInstruction, its instruction index in the ir instruction array
   */
  public static Map computeInstructionIndices(IR ir) {
    Map result = HashMapFactory.make();
    if (ir != null) {
      SSAInstruction[] instructions = ir.getInstructions();
      for (int i = 0; i < instructions.length; i++) {
        SSAInstruction s = instructions[i];
        if (s != null) {
          result.put(s, new Integer(i));
        }
      }
    }
    return result;
  }

  /**
   * Convert a NORMAL or PHI Statement to an SSAInstruction
   */
  private SSAInstruction statement2SSAInstruction(SSAInstruction[] instructions, Statement s) {
    SSAInstruction statement = null;
    switch (s.getKind()) {
      NormalStatement n = (NormalStatement) s;
      statement = instructions[n.getInstructionIndex()];
      break;
    case PHI:
      PhiStatement p = (PhiStatement) s;
      statement = p.getPhi();
      break;
    case PI:
      PiStatement ps = (PiStatement) s;
      statement = ps.getPi();
      break;
    case CATCH:
      GetCaughtExceptionStatement g = (GetCaughtExceptionStatement) s;
      statement = g.getInstruction();
      break;
    default:
      Assertions.UNREACHABLE(s.toString());
    }
    return statement;
  }

  /**
   * Create all nodes in this PDG. Each node is a Statement.
   */
  private void createNodes(Map> ref, ControlDependenceOptions cOptions, IR ir) {

    if (ir != null) {
      createNormalStatements(ir, ref);
      createSpecialStatements(ir);
    }

    createCalleeParams();
    createReturnStatements();

    delegate.addNode(new MethodEntryStatement(node));
    delegate.addNode(new MethodExitStatement(node));
  }

  /**
   * create nodes representing defs of the return values
   * 
   * @param mod the set of heap locations which may be written (transitively) by this node. These are logically parameters in the
   *          SDG.
   * @param dOptions
   */
  private void createReturnStatements() {
    ArrayList list = new ArrayList();
    if (!node.getMethod().getReturnType().equals(TypeReference.Void)) {
      NormalReturnCallee n = new NormalReturnCallee(node);
      delegate.addNode(n);
      list.add(n);
    }
    if (!dOptions.isIgnoreExceptions()) {
      ExceptionalReturnCallee e = new ExceptionalReturnCallee(node);
      delegate.addNode(e);
      list.add(e);
    }
    if (!dOptions.isIgnoreHeap()) {
      for (PointerKey p : mod.get(node)) {
        Statement h = new HeapStatement.HeapReturnCallee(node, p);
        delegate.addNode(h);
        list.add(h);
      }
    }
    returnStatements = new Statement[list.size()];
    list.toArray(returnStatements);
  }

  /**
   * create nodes representing defs of formal parameters
   * 
   * @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG.
   */
  private void createCalleeParams() {
    if (paramCalleeStatements == null) {
      ArrayList list = new ArrayList();
      for (int i = 1; i <= node.getMethod().getNumberOfParameters(); i++) {
        ParamCallee s = new ParamCallee(node, i);
        delegate.addNode(s);
        list.add(s);
      }
      if (!dOptions.isIgnoreHeap()) {
        for (PointerKey p : ref.get(node)) {
          Statement h = new HeapStatement.HeapParamCallee(node, p);
          delegate.addNode(h);
          list.add(h);
        }
      }
      paramCalleeStatements = new Statement[list.size()];
      list.toArray(paramCalleeStatements);
    }
  }

  /**
   * Create nodes corresponding to
   * 
    *
  • phi instructions *
  • getCaughtExceptions *
*/ private void createSpecialStatements(IR ir) { // create a node for instructions which do not correspond to bytecode for (Iterator it = ir.iterateAllInstructions(); it.hasNext();) { SSAInstruction s = it.next(); if (s instanceof SSAPhiInstruction) { delegate.addNode(new PhiStatement(node, (SSAPhiInstruction) s)); } else if (s instanceof SSAGetCaughtExceptionInstruction) { delegate.addNode(new GetCaughtExceptionStatement(node, (SSAGetCaughtExceptionInstruction) s)); } else if (s instanceof SSAPiInstruction) { delegate.addNode(new PiStatement(node, (SSAPiInstruction) s)); } } } /** * Create nodes in the graph corresponding to "normal" (bytecode) instructions */ private void createNormalStatements(IR ir, Map> ref) { // create a node for every normal instruction in the IR SSAInstruction[] instructions = ir.getInstructions(); for (int i = 0; i < instructions.length; i++) { SSAInstruction s = instructions[i]; if (s instanceof SSAGetCaughtExceptionInstruction) { continue; } if (s != null) { final NormalStatement statement = new NormalStatement(node, i); delegate.addNode(statement); if (s instanceof SSAAbstractInvokeInstruction) { callSite2Statement.put(((SSAAbstractInvokeInstruction) s).getCallSite(), statement); addParamPassingStatements(i, ref, ir); } } } } /** * Create nodes in the graph corresponding to in/out parameter passing for a call instruction */ private void addParamPassingStatements(int callIndex, Map> ref, IR ir) { SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction) ir.getInstructions()[callIndex]; Collection params = MapUtil.findOrCreateSet(callerParamStatements, call.getCallSite()); Collection rets = MapUtil.findOrCreateSet(callerReturnStatements, call.getCallSite()); for (int j = 0; j < call.getNumberOfUses(); j++) { Statement st = new ParamCaller(node, callIndex, call.getUse(j)); delegate.addNode(st); params.add(st); } if (!call.getDeclaredResultType().equals(TypeReference.Void)) { Statement st = new NormalReturnCaller(node, callIndex); delegate.addNode(st); rets.add(st); } { if (!dOptions.isIgnoreExceptions()) { Statement st = new ExceptionalReturnCaller(node, callIndex); delegate.addNode(st); rets.add(st); } } if (!dOptions.isIgnoreHeap()) { OrdinalSet uref = unionHeapLocations(cg, node, call, ref); for (PointerKey p : uref) { Statement st = new HeapStatement.HeapParamCaller(node, callIndex, p); delegate.addNode(st); params.add(st); } OrdinalSet umod = unionHeapLocations(cg, node, call, mod); for (PointerKey p : umod) { Statement st = new HeapStatement.HeapReturnCaller(node, callIndex, p); delegate.addNode(st); rets.add(st); } } } /** * @return the set of all locations read by any callee at a call site. */ private OrdinalSet unionHeapLocations(CallGraph cg, CGNode n, SSAAbstractInvokeInstruction call, Map> loc) { BitVectorIntSet bv = new BitVectorIntSet(); for (CGNode t : cg.getPossibleTargets(n, call.getCallSite())) { bv.addAll(loc.get(t).getBackingSet()); } return new OrdinalSet(bv, loc.get(n).getMapping()); } @Override public String toString() { populate(); StringBuffer result = new StringBuffer("PDG for " + node + ":\n"); result.append(super.toString()); return result.toString(); } public Statement[] getParamCalleeStatements() { if (paramCalleeStatements == null) { createCalleeParams(); } Statement[] result = new Statement[paramCalleeStatements.length]; System.arraycopy(paramCalleeStatements, 0, result, 0, result.length); return result; } public Statement[] getReturnStatements() { populate(); Statement[] result = new Statement[returnStatements.length]; System.arraycopy(returnStatements, 0, result, 0, result.length); return result; } public CGNode getCallGraphNode() { return node; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass().equals(obj.getClass())) { return node.equals(((PDG) obj).node); } else { return false; } } @Override public int hashCode() { return 103 * node.hashCode(); } public int getPredNodeCount(Statement N) throws UnimplementedError { populate(); Assertions.UNREACHABLE(); return delegate.getPredNodeCount(N); } public Iterator getPredNodes(Statement N) { populate(); if (!dOptions.isIgnoreHeap()) { computeIncomingHeapDependencies(N); } return delegate.getPredNodes(N); } private void computeIncomingHeapDependencies(Statement N) { switch (N.getKind()) { case NORMAL: NormalStatement st = (NormalStatement) N; if (!(ignoreAllocHeapDefs && st.getInstruction() instanceof SSANewInstruction)) { Collection ref = modRef.getRef(node, heapModel, pa, st.getInstruction(), exclusions); for (PointerKey pk : ref) { createHeapDataDependenceEdges(pk); } } break; case HEAP_PARAM_CALLEE: case HEAP_PARAM_CALLER: case HEAP_RET_CALLEE: case HEAP_RET_CALLER: HeapStatement h = (HeapStatement) N; createHeapDataDependenceEdges(h.getLocation()); } } private void computeOutgoingHeapDependencies(Statement N) { switch (N.getKind()) { case NORMAL: NormalStatement st = (NormalStatement) N; if (!(ignoreAllocHeapDefs && st.getInstruction() instanceof SSANewInstruction)) { Collection mod = modRef.getMod(node, heapModel, pa, st.getInstruction(), exclusions); for (PointerKey pk : mod) { createHeapDataDependenceEdges(pk); } } break; case HEAP_PARAM_CALLEE: case HEAP_PARAM_CALLER: case HEAP_RET_CALLEE: case HEAP_RET_CALLER: HeapStatement h = (HeapStatement) N; createHeapDataDependenceEdges(h.getLocation()); } } public int getSuccNodeCount(Statement N) throws UnimplementedError { populate(); Assertions.UNREACHABLE(); return delegate.getSuccNodeCount(N); } public Iterator getSuccNodes(Statement N) { populate(); if (!dOptions.isIgnoreHeap()) { computeOutgoingHeapDependencies(N); } return delegate.getSuccNodes(N); } public boolean hasEdge(Statement src, Statement dst) throws UnimplementedError { populate(); } return delegate.hasEdge(src, dst); } public void removeNodeAndEdges(Statement N) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public void addNode(Statement n) { Assertions.UNREACHABLE(); } public boolean containsNode(Statement N) { populate(); return delegate.containsNode(N); } public int getNumberOfNodes() { populate(); return delegate.getNumberOfNodes(); } public Iterator iterator() { populate(); return delegate.iterator(); } public void removeNode(Statement n) { Assertions.UNREACHABLE(); } public void addEdge(Statement src, Statement dst) { Assertions.UNREACHABLE(); } public void removeAllIncidentEdges(Statement node) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public void removeEdge(Statement src, Statement dst) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public void removeIncomingEdges(Statement node) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } this.pa = pa; public void removeOutgoingEdges(Statement node) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public int getMaxNumber() { populate(); return delegate.getMaxNumber(); } public Statement getNode(int number) { populate(); return delegate.getNode(number); } public int getNumber(Statement N) { populate(); return delegate.getNumber(N); } public Iterator iterateNodes(IntSet s) { Assertions.UNREACHABLE(); return null; } public IntSet getPredNodeNumbers(Statement node) { Assertions.UNREACHABLE(); return null; } public IntSet getSuccNodeNumbers(Statement node) { Assertions.UNREACHABLE(); return null; } /** BEGIN Custom change: control deps */ public boolean isControlDependend(Statement from, Statement to) { return delegate.hasEdge(from, to, Dependency.CONTROL_DEP); } /** END Custom change: control deps */ } ======= /******************************************************************************* * Copyright (c) 2006 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.ipa.slicer; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine; import com.ibm.wala.cfg.ControlFlowGraph; import com.ibm.wala.cfg.cdg.ControlDependenceGraph; import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.CallGraph; import com.ibm.wala.ipa.callgraph.impl.SetOfClasses; import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis; import com.ibm.wala.ipa.callgraph.propagation.PointerKey; import com.ibm.wala.ipa.cfg.ExceptionPrunedCFG; import com.ibm.wala.ipa.modref.DelegatingExtendedHeapModel; import com.ibm.wala.ipa.modref.ExtendedHeapModel; import com.ibm.wala.ipa.modref.ModRef; import com.ibm.wala.ipa.slicer.Slicer.ControlDependenceOptions; import com.ibm.wala.ipa.slicer.Slicer.DataDependenceOptions; import com.ibm.wala.ipa.slicer.Statement.Kind; import com.ibm.wala.ssa.DefUse; import com.ibm.wala.ssa.IR; import com.ibm.wala.ssa.ISSABasicBlock; import com.ibm.wala.ssa.SSAAbstractInvokeInstruction; import com.ibm.wala.ssa.SSAAbstractThrowInstruction; import com.ibm.wala.ssa.SSAArrayLengthInstruction; import com.ibm.wala.ssa.SSAArrayReferenceInstruction; import com.ibm.wala.ssa.SSACFG; import com.ibm.wala.ssa.SSACheckCastInstruction; import com.ibm.wala.ssa.SSAFieldAccessInstruction; import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction; import com.ibm.wala.ssa.SSAInstanceofInstruction; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSANewInstruction; import com.ibm.wala.ssa.SSAPhiInstruction; import com.ibm.wala.ssa.SSAPiInstruction; import com.ibm.wala.ssa.SSAReturnInstruction; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.collections.Filter; import com.ibm.wala.util.collections.FilterIterator; import com.ibm.wala.util.collections.HashMapFactory; import com.ibm.wala.util.collections.HashSetFactory; import com.ibm.wala.util.collections.Iterator2Collection; import com.ibm.wala.util.collections.Iterator2Iterable; import com.ibm.wala.util.collections.MapUtil; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.debug.UnimplementedError; import com.ibm.wala.util.graph.NumberedGraph; import com.ibm.wala.util.graph.dominators.Dominators; import com.ibm.wala.util.graph.impl.SlowSparseNumberedGraph; import com.ibm.wala.util.intset.BitVectorIntSet; import com.ibm.wala.util.intset.IntIterator; import com.ibm.wala.util.intset.IntSet; import com.ibm.wala.util.intset.OrdinalSet; /** * Program dependence graph for a single call graph node */ public class PDG implements NumberedGraph { private final SlowSparseNumberedGraph delegate = SlowSparseNumberedGraph.make(); private final static boolean VERBOSE = false; private final CGNode node; private Statement[] paramCalleeStatements; private Statement[] returnStatements; /** * TODO: using CallSiteReference is sloppy. clean it up. */ private final Map callSite2Statement = HashMapFactory.make(); private final Map> callerParamStatements = HashMapFactory.make(); private final Map> callerReturnStatements = HashMapFactory.make(); private final HeapExclusions exclusions; private final Collection locationsHandled = HashSetFactory.make(); private final PointerAnalysis pa; private final ExtendedHeapModel heapModel; private final Map> mod; private final DataDependenceOptions dOptions; private final ControlDependenceOptions cOptions; private final CallGraph cg; private final ModRef modRef; private final Map> ref; private final boolean ignoreAllocHeapDefs; private boolean isPopulated = false; /** * @param mod the set of heap locations which may be written (transitively) by this node. These are logically return values in the * SDG. * @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG. * @throws IllegalArgumentException if node is null */ public PDG(final CGNode node, PointerAnalysis pa, Map> mod, Map> ref, DataDependenceOptions dOptions, ControlDependenceOptions cOptions, HeapExclusions exclusions, CallGraph cg, ModRef modRef) { this(node, pa, mod, ref, dOptions, cOptions, exclusions, cg, modRef, false); } /** * @param mod the set of heap locations which may be written (transitively) by this node. These are logically return values in the * SDG. * @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG. * @throws IllegalArgumentException if node is null */ public PDG(final CGNode node, PointerAnalysis pa, Map> mod, Map> ref, DataDependenceOptions dOptions, ControlDependenceOptions cOptions, HeapExclusions exclusions, CallGraph cg, ModRef modRef, boolean ignoreAllocHeapDefs) { super(); if (node == null) { throw new IllegalArgumentException("node is null"); } this.cg = cg; this.node = node; this.heapModel = pa == null ? null : new DelegatingExtendedHeapModel(pa.getHeapModel()); this.dOptions = dOptions; this.cOptions = cOptions; this.mod = mod; this.exclusions = exclusions; this.modRef = modRef; this.ref = ref; this.ignoreAllocHeapDefs = ignoreAllocHeapDefs; } /** * WARNING: Since we're using a {@link HashMap} of {@link SSAInstruction}s, and equals() of {@link SSAInstruction} assumes a * canonical representative for each instruction, we must ensure that we use the same IR object throughout * initialization!! */ private void populate() { if (!isPopulated) { // ensure that we keep the single, canonical IR live throughout initialization, while the instructionIndices map // is live. IR ir = node.getIR(); isPopulated = true; Map instructionIndices = computeInstructionIndices(ir); createNodes(ref, cOptions, ir); createScalarEdges(cOptions, ir, instructionIndices); } } private void createScalarEdges(ControlDependenceOptions cOptions, IR ir, Map instructionIndices) { createScalarDataDependenceEdges(ir, instructionIndices); createControlDependenceEdges(cOptions, ir, instructionIndices); } /** * return the set of all PARAM_CALLER and HEAP_PARAM_CALLER statements associated with a given call */ public Set getCallerParamStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException { if (call == null) { throw new IllegalArgumentException("call == null"); } populate(); return callerParamStatements.get(call.getCallSite()); } /** * return the set of all PARAM_CALLER, HEAP_PARAM_CALLER, and NORMAL statements (i.e., the actual call statement) associated with * a given call */ public Set getCallStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException { Set callerParamStatements = getCallerParamStatements(call); Set result = HashSetFactory.make(callerParamStatements.size() + 1); result.addAll(callerParamStatements); result.add(callSite2Statement.get(call.getCallSite())); return result; } /** * return the set of all NORMAL_RETURN_CALLER and HEAP_RETURN_CALLER statements associated with a given call. */ public Set getCallerReturnStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException { if (call == null) { throw new IllegalArgumentException("call == null"); } populate(); return callerReturnStatements.get(call.getCallSite()); } /** * Create all control dependence edges in this PDG. */ private void createControlDependenceEdges(ControlDependenceOptions cOptions, IR ir, Map instructionIndices) { if (cOptions.equals(ControlDependenceOptions.NONE)) { return; } if (ir == null) { return; } ControlFlowGraph controlFlowGraph = ir.getControlFlowGraph(); if (cOptions.equals(ControlDependenceOptions.NO_EXCEPTIONAL_EDGES)) { controlFlowGraph = ExceptionPrunedCFG.make(controlFlowGraph); // In case the CFG has no nodes left because the only control dependencies // were // exceptional, simply return because at this point there are no nodes. // Otherwise, later this may raise an Exception. if (controlFlowGraph.getNumberOfNodes() == 0) { return; } } else { Assertions.productionAssertion(cOptions.equals(ControlDependenceOptions.FULL)); } ControlDependenceGraph cdg = new ControlDependenceGraph( controlFlowGraph); } else { for (ISSABasicBlock bb : cdg) { if (bb.isExitBlock()) { // nothing should be control-dependent on the exit block. continue; } Statement src = null; if (bb.isEntryBlock()) { src = new MethodEntryStatement(node); } else { SSAInstruction s = ir.getInstructions()[bb.getLastInstructionIndex()]; if (s == null) { // should have no control dependent successors. // leave src null. } else { src = ssaInstruction2Statement(s, ir, instructionIndices); // add edges from call statements to parameter passing and return // SJF: Alexey and I think that we should just define ParamStatements // as // being control dependent on nothing ... they only represent pure // data dependence. So, I'm commenting out the following. // if (s instanceof SSAAbstractInvokeInstruction) { // SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction) // s; // for (Statement st : callerParamStatements.get(call.getCallSite())) // { // addEdge(src, st); // } // for (Statement st : callerReturnStatements.get(call.getCallSite())) // { // addEdge(src, st); // } // } } } // add edges for every control-dependent statement in the IR, if there are // any // control-dependent successors if (src != null) { for (Iterator succ = cdg.getSuccNodes(bb); succ.hasNext();) { ISSABasicBlock bb2 = succ.next(); for (Iterator it2 = bb2.iterator(); it2.hasNext();) { SSAInstruction st = it2.next(); if (st != null) { Statement dest = ssaInstruction2Statement(st, ir, instructionIndices); assert src != null; delegate.addEdge(src, dest); } } } } } // the CDG does not represent control dependences from the entry node. // add these manually if (hasBasePointer(use)) { // We add control dependences to all instructions in all basic blocks B that _must_ execute. // B is the set of blocks that dominate the exit basic block Statement methodEntry = new MethodEntryStatement(node); Dominators dom = Dominators.make(controlFlowGraph, controlFlowGraph.entry()); for (ISSABasicBlock exitDom : Iterator2Iterable.make(dom.dominators(controlFlowGraph.exit()))) { for (SSAInstruction st : exitDom) { Statement dest = ssaInstruction2Statement(st, ir, instructionIndices); delegate.addEdge(methodEntry, dest); } } // add CD from method entry to all callee parameter assignments // SJF: Alexey and I think that we should just define ParamStatements as // being control dependent on nothing ... they only represent pure // data dependence. So, I'm commenting out the following. // for (int i = 0; i < paramCalleeStatements.length; i++) { // addEdge(methodEntry, paramCalleeStatements[i]); // } /** * JTD: While phi nodes live in a particular basic block, they represent a meet of values from multiple blocks. Hence, they are * really like multiple statements that are control dependent in the manner of the predecessor blocks. When the slicer is * following both data and control dependences, it therefore seems right to add control dependence edges to represent how a phi * node depends on predecessor blocks. */ if (!dOptions.equals(DataDependenceOptions.NONE)) { for (ISSABasicBlock bb : cdg) { for (Iterator ps = bb.iteratePhis(); ps.hasNext();) { SSAPhiInstruction phi = ps.next(); Statement phiSt = ssaInstruction2Statement(phi, ir, instructionIndices); int phiUseIndex = 0; for (Iterator preds = controlFlowGraph.getPredNodes(bb); preds.hasNext();) { ISSABasicBlock pb = preds.next(); int use = phi.getUse(phiUseIndex); if (use == AbstractIntStackMachine.TOP) { // the predecessor is part of some infeasible bytecode. we probably don't want slices to include such code, so ignore. continue; } if (controlFlowGraph.getSuccNodeCount(pb) > 1) { // in this case, there is more than one edge from the // predecessor block, hence the phi node actually // depends on the last instruction in the previous // block, rather than having the same dependences as // statements in that block. SSAInstruction pss = ir.getInstructions()[pb.getLastInstructionIndex()]; assert pss != null; Statement pst = ssaInstruction2Statement(pss, ir, instructionIndices); delegate.addEdge(pst, phiSt); } else { for (Iterator cdps = cdg.getPredNodes(pb); cdps.hasNext();) { ISSABasicBlock cpb = cdps.next(); SSAInstruction cps = ir.getInstructions()[cpb.getLastInstructionIndex()]; assert cps != null : "unexpected null final instruction for CDG predecessor " + cpb + " in node " + node; Statement cpst = ssaInstruction2Statement(cps, ir, instructionIndices); delegate.addEdge(cpst, phiSt); } } phiUseIndex++; } } } } } /** * Create all data dependence edges in this PDG. * * Scalar dependences are taken from SSA def-use information. * * Heap dependences are computed by a reaching defs analysis. * * @param pa * @param mod */ private void createScalarDataDependenceEdges(IR ir, Map instructionIndices) { if (dOptions.equals(DataDependenceOptions.NONE)) { return; } if (ir == null) { return; } // this is tricky .. I'm explicitly creating a new DefUse to make sure it refers to the instructions we need from // the "one true" ir of the moment. DefUse DU = new DefUse(ir); SSAInstruction[] instructions = ir.getInstructions(); // // TODO: teach some other bit of code about the uses of // GetCaughtException, and then delete this code. // if (!dOptions.isIgnoreExceptions()) { for (ISSABasicBlock bb : ir.getControlFlowGraph()) { if (bb.isCatchBlock()) { SSACFG.ExceptionHandlerBasicBlock ehbb = (SSACFG.ExceptionHandlerBasicBlock) bb; if (ehbb.getCatchInstruction() != null) { Statement c = ssaInstruction2Statement(ehbb.getCatchInstruction(), ir, instructionIndices); for (ISSABasicBlock pb : ir.getControlFlowGraph().getExceptionalPredecessors(ehbb)) { SSAInstruction st = instructions[pb.getLastInstructionIndex()]; if (st instanceof SSAAbstractInvokeInstruction) { delegate.addEdge(new ExceptionalReturnCaller(node, pb.getLastInstructionIndex()), c); } else if (st instanceof SSAAbstractThrowInstruction) { delegate.addEdge(ssaInstruction2Statement(st, ir, instructionIndices), c); } } } } } } for (Iterator it = iterator(); it.hasNext();) { Statement s = it.next(); switch (s.getKind()) { case NORMAL: case CATCH: case PI: case PHI: { SSAInstruction statement = statement2SSAInstruction(instructions, s); // note that data dependencies from invoke instructions will pass // interprocedurally if (!(statement instanceof SSAAbstractInvokeInstruction)) { if (dOptions.isTerminateAtCast() && (statement instanceof SSACheckCastInstruction)) { break; } if (dOptions.isTerminateAtCast() && (statement instanceof SSAInstanceofInstruction)) { break; } // add edges from this statement to every use of the def of this // statement for (int i = 0; i < statement.getNumberOfDefs(); i++) { int def = statement.getDef(i); for (Iterator it2 = DU.getUses(def); it2.hasNext();) { SSAInstruction use = it2.next(); if (dOptions.isIgnoreBasePtrs()) { if (use instanceof SSANewInstruction) { // cut out array length parameters continue; } if (hasBasePointer(use)) { int base = getBasePointer(use); if (def == base) { // skip the edge to the base pointer continue; } if (use instanceof SSAArrayReferenceInstruction) { SSAArrayReferenceInstruction arr = (SSAArrayReferenceInstruction) use; if (def == arr.getIndex()) { // skip the edge to the array index continue; } } } } Statement u = ssaInstruction2Statement(use, ir, instructionIndices); delegate.addEdge(s, u); } } } break; } case EXC_RET_CALLER: case NORMAL_RET_CALLER: case PARAM_CALLEE: { if (dOptions.isIgnoreExceptions()) { assert !s.getKind().equals(Kind.EXC_RET_CALLER); } ValueNumberCarrier a = (ValueNumberCarrier) s; for (Iterator it2 = DU.getUses(a.getValueNumber()); it2.hasNext();) { SSAInstruction use = it2.next(); if (dOptions.isIgnoreBasePtrs()) { if (use instanceof SSANewInstruction) { // cut out array length parameters continue; int base = getBasePointer(use); if (a.getValueNumber() == base) { // skip the edge to the base pointer continue; } if (use instanceof SSAArrayReferenceInstruction) { SSAArrayReferenceInstruction arr = (SSAArrayReferenceInstruction) use; if (a.getValueNumber() == arr.getIndex()) { // skip the edge to the array index continue; } } } } Statement u = ssaInstruction2Statement(use, ir, instructionIndices); delegate.addEdge(s, u); } break; } case NORMAL_RET_CALLEE: for (NormalStatement ret : computeReturnStatements(ir)) { delegate.addEdge(ret, s); } break; case EXC_RET_CALLEE: if (dOptions.isIgnoreExceptions()) { Assertions.UNREACHABLE(); } // TODO: this is overly conservative. deal with catch blocks? for (IntIterator ii = getPEIs(ir).intIterator(); ii.hasNext();) { int index = ii.next(); SSAInstruction pei = ir.getInstructions()[index]; if (dOptions.isTerminateAtCast() && (pei instanceof SSACheckCastInstruction)) { continue; } if (pei instanceof SSAAbstractInvokeInstruction) { Statement st = new ExceptionalReturnCaller(node, index); delegate.addEdge(st, s); delegate.addEdge(new NormalStatement(node, index), s); } } break; case PARAM_CALLER: { ParamCaller pac = (ParamCaller) s; int vn = pac.getValueNumber(); // note that if the caller is the fake root method and the parameter // type is primitive, // it's possible to have a value number of -1. If so, just ignore it. if (vn > -1) { if (ir.getSymbolTable().isParameter(vn)) { Statement a = new ParamCallee(node, vn); delegate.addEdge(a, pac); } else { SSAInstruction d = DU.getDef(vn); if (dOptions.isTerminateAtCast() && (d instanceof SSACheckCastInstruction)) { break; } if (d != null) { if (d instanceof SSAAbstractInvokeInstruction) { SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction) d; if (vn == call.getException()) { Statement st = new ExceptionalReturnCaller(node, instructionIndices.get(d)); delegate.addEdge(st, pac); } else { Statement st = new NormalReturnCaller(node, instructionIndices.get(d)); delegate.addEdge(st, pac); } } else { Statement ds = ssaInstruction2Statement(d, ir, instructionIndices); delegate.addEdge(ds, pac); } } } } } break; case HEAP_RET_CALLEE: case HEAP_RET_CALLER: case HEAP_PARAM_CALLER: case HEAP_PARAM_CALLEE: case METHOD_ENTRY: case METHOD_EXIT: // do nothing break; default: Assertions.UNREACHABLE(s.toString()); break; } } } private static class SingletonSet extends SetOfClasses implements Serializable { /* Serial version */ private static final long serialVersionUID = -3256390509887654324L; private final TypeReference t; SingletonSet(TypeReference t) { this.t = t; } @Override public void add(IClass klass) { Assertions.UNREACHABLE(); } @Override public boolean contains(String klassName) { Assertions.UNREACHABLE(); return false; } @Override public boolean contains(TypeReference klass) { return t.equals(klass); } } private static class SetComplement extends SetOfClasses implements Serializable { /* Serial version */ private static final long serialVersionUID = -3256390509887654323L; private final SetOfClasses set; SetComplement(SetOfClasses set) { this.set = set; } static SetComplement complement(SetOfClasses set) { return new SetComplement(set); } @Override public void add(IClass klass) { Assertions.UNREACHABLE(); } @Override public boolean contains(String klassName) { Assertions.UNREACHABLE(); return false; } @Override public boolean contains(TypeReference klass) { return !set.contains(klass); } } /** * Create heap data dependence edges in this PDG relevant to a particular {@link PointerKey}. */ private void createHeapDataDependenceEdges(final PointerKey pk) { if (locationsHandled.contains(pk)) { return; } else { locationsHandled.add(pk); } if (dOptions.isIgnoreHeap() || (exclusions != null && exclusions.excludes(pk))) { return; } TypeReference t = HeapExclusions.getType(pk); if (t == null) { return; } // It's OK to create a new IR here; we're not keeping any hashing live up to this point IR ir = node.getIR(); if (ir == null) { return; } if (VERBOSE) { System.err.println("Location " + pk); } // in reaching defs calculation, exclude heap statements that are // irrelevant. Filter f = new Filter() { public boolean accepts(Object o) { if (o instanceof HeapStatement) { HeapStatement h = (HeapStatement) o; return h.getLocation().equals(pk); } else { return true; } } }; Collection relevantStatements = Iterator2Collection.toSet(new FilterIterator(iterator(), f)); Map> heapReachingDefs = new HeapReachingDefs(modRef).computeReachingDefs(node, ir, pa, mod, relevantStatements, new HeapExclusions(SetComplement.complement(new SingletonSet(t))), cg); for (Statement st : heapReachingDefs.keySet()) { switch (st.getKind()) { case NORMAL: case CATCH: case PHI: case PI: { OrdinalSet defs = heapReachingDefs.get(st); if (defs != null) { for (Statement def : defs) { delegate.addEdge(def, st); } } } break; case EXC_RET_CALLER: case NORMAL_RET_CALLER: case PARAM_CALLEE: case NORMAL_RET_CALLEE: case PARAM_CALLER: case EXC_RET_CALLEE: break; case HEAP_RET_CALLEE: case HEAP_RET_CALLER: case HEAP_PARAM_CALLER: { OrdinalSet defs = heapReachingDefs.get(st); if (defs != null) { for (Statement def : defs) { delegate.addEdge(def, st); } } break; } case HEAP_PARAM_CALLEE: case METHOD_ENTRY: case METHOD_EXIT: // do nothing .. there are no incoming edges break; default: Assertions.UNREACHABLE(st.toString()); break; } } } private boolean hasBasePointer(SSAInstruction use) { if (use instanceof SSAFieldAccessInstruction) { SSAFieldAccessInstruction f = (SSAFieldAccessInstruction) use; return !f.isStatic(); } else if (use instanceof SSAArrayReferenceInstruction) { return true; } else if (use instanceof SSAArrayLengthInstruction) { return true; } else { return false; } } private int getBasePointer(SSAInstruction use) { if (use instanceof SSAFieldAccessInstruction) { SSAFieldAccessInstruction f = (SSAFieldAccessInstruction) use; return f.getRef(); } else if (use instanceof SSAArrayReferenceInstruction) { SSAArrayReferenceInstruction a = (SSAArrayReferenceInstruction) use; return a.getArrayRef(); } else if (use instanceof SSAArrayLengthInstruction) { SSAArrayLengthInstruction s = (SSAArrayLengthInstruction) use; return s.getArrayRef(); } else { Assertions.UNREACHABLE("BOOM"); return -1; } } /** * @return Statements representing each return instruction in the ir */ private Collection computeReturnStatements(final IR ir) { Filter filter = new Filter() { public boolean accepts(Object o) { if (o instanceof NormalStatement) { NormalStatement s = (NormalStatement) o; SSAInstruction st = ir.getInstructions()[s.getInstructionIndex()]; return st instanceof SSAReturnInstruction; } else { return false; } } }; return Iterator2Collection.toSet(new FilterIterator(iterator(), filter)); } /** * @return {@link IntSet} representing instruction indices of each PEI in the ir */ private IntSet getPEIs(final IR ir) { BitVectorIntSet result = new BitVectorIntSet(); for (int i = 0; i < ir.getInstructions().length; i++) { if (ir.getInstructions()[i] != null && ir.getInstructions()[i].isPEI()) { result.add(i); } } return result; } /** * Wrap an {@link SSAInstruction} in a {@link Statement}. WARNING: Since we're using a {@link HashMap} of {@link SSAInstruction}s, * and equals() of {@link SSAInstruction} assumes a canonical representative for each instruction, we must ensure that we * use the same IR object throughout initialization!! */ private Statement ssaInstruction2Statement(SSAInstruction s, IR ir, Map instructionIndices) { return ssaInstruction2Statement(node, s, instructionIndices, ir); } params.add(st); public static synchronized Statement ssaInstruction2Statement(CGNode node, SSAInstruction s, Map instructionIndices, IR ir) { if (node == null) { throw new IllegalArgumentException("null node"); } if (s == null) { throw new IllegalArgumentException("null s"); } if (s instanceof SSAPhiInstruction) { SSAPhiInstruction phi = (SSAPhiInstruction) s; return new PhiStatement(node, phi); } else if (s instanceof SSAPiInstruction) { SSAPiInstruction pi = (SSAPiInstruction) s; return new PiStatement(node, pi); } else if (s instanceof SSAGetCaughtExceptionInstruction) { return new GetCaughtExceptionStatement(node, ((SSAGetCaughtExceptionInstruction) s)); } else { Integer x = instructionIndices.get(s); if (x == null) { Assertions.UNREACHABLE(s.toString() + "\nnot found in map of\n" + ir); } return new NormalStatement(node, x.intValue()); } } /** * @return for each SSAInstruction, its instruction index in the ir instruction array */ public static Map computeInstructionIndices(IR ir) { Map result = HashMapFactory.make(); if (ir != null) { SSAInstruction[] instructions = ir.getInstructions(); for (int i = 0; i < instructions.length; i++) { SSAInstruction s = instructions[i]; if (s != null) { result.put(s, new Integer(i)); } } } return result; } /** * Convert a NORMAL or PHI Statement to an SSAInstruction */ private SSAInstruction statement2SSAInstruction(SSAInstruction[] instructions, Statement s) { SSAInstruction statement = null; switch (s.getKind()) { case NORMAL: NormalStatement n = (NormalStatement) s; statement = instructions[n.getInstructionIndex()]; break; case PHI: PhiStatement p = (PhiStatement) s; statement = p.getPhi(); break; case PI: PiStatement ps = (PiStatement) s; statement = ps.getPi(); break; case CATCH: GetCaughtExceptionStatement g = (GetCaughtExceptionStatement) s; statement = g.getInstruction(); break; default: Assertions.UNREACHABLE(s.toString()); } return statement; } /** * Create all nodes in this PDG. Each node is a Statement. */ private void createNodes(Map> ref, ControlDependenceOptions cOptions, IR ir) { if (ir != null) { createNormalStatements(ir, ref); createSpecialStatements(ir); } createCalleeParams(); createReturnStatements(); delegate.addNode(new MethodEntryStatement(node)); delegate.addNode(new MethodExitStatement(node)); } /** * create nodes representing defs of the return values * * @param mod the set of heap locations which may be written (transitively) by this node. These are logically parameters in the * SDG. * @param dOptions */ private void createReturnStatements() { ArrayList list = new ArrayList(); if (!node.getMethod().getReturnType().equals(TypeReference.Void)) { NormalReturnCallee n = new NormalReturnCallee(node); delegate.addNode(n); list.add(n); } if (!dOptions.isIgnoreExceptions()) { ExceptionalReturnCallee e = new ExceptionalReturnCallee(node); delegate.addNode(e); list.add(e); } if (!dOptions.isIgnoreHeap()) { for (PointerKey p : mod.get(node)) { Statement h = new HeapStatement.HeapReturnCallee(node, p); delegate.addNode(h); list.add(h); } } returnStatements = new Statement[list.size()]; list.toArray(returnStatements); } /** * create nodes representing defs of formal parameters * * @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG. */ private void createCalleeParams() { if (paramCalleeStatements == null) { ArrayList list = new ArrayList(); for (int i = 1; i <= node.getMethod().getNumberOfParameters(); i++) { ParamCallee s = new ParamCallee(node, i); delegate.addNode(s); list.add(s); } if (!dOptions.isIgnoreHeap()) { for (PointerKey p : ref.get(node)) { Statement h = new HeapStatement.HeapParamCallee(node, p); delegate.addNode(h); list.add(h); } } paramCalleeStatements = new Statement[list.size()]; list.toArray(paramCalleeStatements); } } /** * Create nodes corresponding to *
    *
  • phi instructions *
  • getCaughtExceptions *
*/ private void createSpecialStatements(IR ir) { // create a node for instructions which do not correspond to bytecode for (Iterator it = ir.iterateAllInstructions(); it.hasNext();) { SSAInstruction s = it.next(); if (s instanceof SSAPhiInstruction) { delegate.addNode(new PhiStatement(node, (SSAPhiInstruction) s)); } else if (s instanceof SSAGetCaughtExceptionInstruction) { delegate.addNode(new GetCaughtExceptionStatement(node, (SSAGetCaughtExceptionInstruction) s)); } else if (s instanceof SSAPiInstruction) { delegate.addNode(new PiStatement(node, (SSAPiInstruction) s)); } } } /** * Create nodes in the graph corresponding to "normal" (bytecode) instructions */ private void createNormalStatements(IR ir, Map> ref) { // create a node for every normal instruction in the IR SSAInstruction[] instructions = ir.getInstructions(); for (int i = 0; i < instructions.length; i++) { SSAInstruction s = instructions[i]; if (s instanceof SSAGetCaughtExceptionInstruction) { continue; } if (s != null) { final NormalStatement statement = new NormalStatement(node, i); delegate.addNode(statement); if (s instanceof SSAAbstractInvokeInstruction) { callSite2Statement.put(((SSAAbstractInvokeInstruction) s).getCallSite(), statement); addParamPassingStatements(i, ref, ir); } } } } /** * Create nodes in the graph corresponding to in/out parameter passing for a call instruction */ private void addParamPassingStatements(int callIndex, Map> ref, IR ir) { SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction) ir.getInstructions()[callIndex]; Collection params = MapUtil.findOrCreateSet(callerParamStatements, call.getCallSite()); Collection rets = MapUtil.findOrCreateSet(callerReturnStatements, call.getCallSite()); for (int j = 0; j < call.getNumberOfUses(); j++) { Statement st = new ParamCaller(node, callIndex, call.getUse(j)); delegate.addNode(st); } if (!call.getDeclaredResultType().equals(TypeReference.Void)) { Statement st = new NormalReturnCaller(node, callIndex); delegate.addNode(st); rets.add(st); } { if (!dOptions.isIgnoreExceptions()) { Statement st = new ExceptionalReturnCaller(node, callIndex); delegate.addNode(st); rets.add(st); } } if (!dOptions.isIgnoreHeap()) { OrdinalSet uref = unionHeapLocations(cg, node, call, ref); for (PointerKey p : uref) { Statement st = new HeapStatement.HeapParamCaller(node, callIndex, p); delegate.addNode(st); params.add(st); } OrdinalSet umod = unionHeapLocations(cg, node, call, mod); for (PointerKey p : umod) { Statement st = new HeapStatement.HeapReturnCaller(node, callIndex, p); delegate.addNode(st); rets.add(st); } } } /** * @return the set of all locations read by any callee at a call site. */ private OrdinalSet unionHeapLocations(CallGraph cg, CGNode n, SSAAbstractInvokeInstruction call, Map> loc) { BitVectorIntSet bv = new BitVectorIntSet(); for (CGNode t : cg.getPossibleTargets(n, call.getCallSite())) { bv.addAll(loc.get(t).getBackingSet()); } return new OrdinalSet(bv, loc.get(n).getMapping()); } @Override public String toString() { populate(); StringBuffer result = new StringBuffer("PDG for " + node + ":\n"); result.append(super.toString()); return result.toString(); } public Statement[] getParamCalleeStatements() { if (paramCalleeStatements == null) { createCalleeParams(); } Statement[] result = new Statement[paramCalleeStatements.length]; System.arraycopy(paramCalleeStatements, 0, result, 0, result.length); return result; } public Statement[] getReturnStatements() { populate(); Statement[] result = new Statement[returnStatements.length]; System.arraycopy(returnStatements, 0, result, 0, result.length); return result; } public CGNode getCallGraphNode() { return node; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass().equals(obj.getClass())) { return node.equals(((PDG) obj).node); } else { return false; } } @Override public int hashCode() { return 103 * node.hashCode(); } public int getPredNodeCount(Statement N) throws UnimplementedError { populate(); Assertions.UNREACHABLE(); return delegate.getPredNodeCount(N); } public Iterator getPredNodes(Statement N) { populate(); if (!dOptions.isIgnoreHeap()) { computeIncomingHeapDependencies(N); } return delegate.getPredNodes(N); } private void computeIncomingHeapDependencies(Statement N) { switch (N.getKind()) { case NORMAL: NormalStatement st = (NormalStatement) N; if (!(ignoreAllocHeapDefs && st.getInstruction() instanceof SSANewInstruction)) { Collection ref = modRef.getRef(node, heapModel, pa, st.getInstruction(), exclusions); for (PointerKey pk : ref) { createHeapDataDependenceEdges(pk); } } break; case HEAP_PARAM_CALLEE: case HEAP_PARAM_CALLER: case HEAP_RET_CALLEE: case HEAP_RET_CALLER: HeapStatement h = (HeapStatement) N; createHeapDataDependenceEdges(h.getLocation()); } } private void computeOutgoingHeapDependencies(Statement N) { switch (N.getKind()) { case NORMAL: NormalStatement st = (NormalStatement) N; if (!(ignoreAllocHeapDefs && st.getInstruction() instanceof SSANewInstruction)) { Collection mod = modRef.getMod(node, heapModel, pa, st.getInstruction(), exclusions); for (PointerKey pk : mod) { createHeapDataDependenceEdges(pk); } } break; case HEAP_PARAM_CALLEE: case HEAP_PARAM_CALLER: case HEAP_RET_CALLEE: case HEAP_RET_CALLER: HeapStatement h = (HeapStatement) N; createHeapDataDependenceEdges(h.getLocation()); } } public int getSuccNodeCount(Statement N) throws UnimplementedError { populate(); Assertions.UNREACHABLE(); return delegate.getSuccNodeCount(N); } public Iterator getSuccNodes(Statement N) { populate(); if (!dOptions.isIgnoreHeap()) { computeOutgoingHeapDependencies(N); } return delegate.getSuccNodes(N); } public boolean hasEdge(Statement src, Statement dst) throws UnimplementedError { populate(); return delegate.hasEdge(src, dst); } public void removeNodeAndEdges(Statement N) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public void addNode(Statement n) { Assertions.UNREACHABLE(); } public boolean containsNode(Statement N) { populate(); return delegate.containsNode(N); } public int getNumberOfNodes() { populate(); return delegate.getNumberOfNodes(); } public Iterator iterator() { populate(); return delegate.iterator(); } public void removeNode(Statement n) { Assertions.UNREACHABLE(); } public void addEdge(Statement src, Statement dst) { Assertions.UNREACHABLE(); } public void removeAllIncidentEdges(Statement node) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public void removeEdge(Statement src, Statement dst) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public void removeIncomingEdges(Statement node) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public void removeOutgoingEdges(Statement node) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public int getMaxNumber() { populate(); return delegate.getMaxNumber(); } public Statement getNode(int number) { populate(); return delegate.getNode(number); } public int getNumber(Statement N) { populate(); return delegate.getNumber(N); } public Iterator iterateNodes(IntSet s) { Assertions.UNREACHABLE(); return null; } public IntSet getPredNodeNumbers(Statement node) { Assertions.UNREACHABLE(); return null; } public IntSet getSuccNodeNumbers(Statement node) { Assertions.UNREACHABLE(); return null; } } >>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
    }
/*******************************************************************************
 * Copyright (c) 2006 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.ipa.slicer;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.cfg.cdg.ControlDependenceGraph;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.impl.SetOfClasses;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
import com.ibm.wala.ipa.cfg.ExceptionPrunedCFG;
import com.ibm.wala.ipa.modref.DelegatingExtendedHeapModel;
import com.ibm.wala.ipa.modref.ExtendedHeapModel;
import com.ibm.wala.ipa.modref.ModRef;
import com.ibm.wala.ipa.slicer.Slicer.ControlDependenceOptions;
import com.ibm.wala.ipa.slicer.Slicer.DataDependenceOptions;
import com.ibm.wala.ipa.slicer.Statement.Kind;
   */
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAAbstractThrowInstruction;
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
import com.ibm.wala.ssa.SSAArrayReferenceInstruction;
import com.ibm.wala.ssa.SSACFG;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAFieldAccessInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAInstanceofInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.Filter;
import com.ibm.wala.util.collections.FilterIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.Iterator2Iterable;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.graph.NumberedGraph;
import com.ibm.wala.util.graph.dominators.Dominators;
import com.ibm.wala.util.graph.labeled.SlowSparseNumberedLabeledGraph;
import com.ibm.wala.util.intset.BitVectorIntSet;
import com.ibm.wala.util.intset.IntIterator;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.OrdinalSet;

/**
 * Program dependence graph for a single call graph node
 */
public class PDG implements NumberedGraph {

/** BEGIN Custom change: control deps */                
  public enum Dependency {CONTROL_DEP, DATA_AND_CONTROL_DEP};
  
  private final SlowSparseNumberedLabeledGraph delegate =
    new SlowSparseNumberedLabeledGraph(Dependency.DATA_AND_CONTROL_DEP);
/** END Custom change: control deps */                

  private final static boolean VERBOSE = false;

  private final CGNode node;

  private Statement[] paramCalleeStatements;

  private Statement[] returnStatements;

  /**
   * TODO: using CallSiteReference is sloppy. clean it up.
   */

  private final Map callSite2Statement = HashMapFactory.make();

  private final Map> callerParamStatements = HashMapFactory.make();

  private final Map> callerReturnStatements = HashMapFactory.make();

  private final HeapExclusions exclusions;

  private final Collection locationsHandled = HashSetFactory.make();

  private final PointerAnalysis pa;

  private final ExtendedHeapModel heapModel;

  private final Map> mod;

  private final DataDependenceOptions dOptions;

  private final ControlDependenceOptions cOptions;

  private final CallGraph cg;

  private final ModRef modRef;

  private final Map> ref;

  private final boolean ignoreAllocHeapDefs;

  private boolean isPopulated = false;

  /**
   * @param mod the set of heap locations which may be written (transitively) by this node. These are logically return values in the
   *          SDG.
   * @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG.
   * @throws IllegalArgumentException if node is null
   */
  public PDG(final CGNode node, PointerAnalysis pa, Map> mod,
      Map> ref, DataDependenceOptions dOptions, ControlDependenceOptions cOptions,
      HeapExclusions exclusions, CallGraph cg, ModRef modRef) {
    this(node, pa, mod, ref, dOptions, cOptions, exclusions, cg, modRef, false);
  }

  /**
   * @param mod the set of heap locations which may be written (transitively) by this node. These are logically return values in the
   *          SDG.
   * @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG.
   * @throws IllegalArgumentException if node is null
  public PDG(final CGNode node, PointerAnalysis pa, Map> mod,
      Map> ref, DataDependenceOptions dOptions, ControlDependenceOptions cOptions,
      HeapExclusions exclusions, CallGraph cg, ModRef modRef, boolean ignoreAllocHeapDefs) {

    super();
    if (node == null) {
      throw new IllegalArgumentException("node is null");
    }
    this.cg = cg;
    this.node = node;
    this.heapModel = pa == null ? null : new DelegatingExtendedHeapModel(pa.getHeapModel());
    this.pa = pa;
    this.dOptions = dOptions;
    this.cOptions = cOptions;
    this.mod = mod;
    this.exclusions = exclusions;
    this.modRef = modRef;
    this.ref = ref;
    this.ignoreAllocHeapDefs = ignoreAllocHeapDefs;
  }

  /**
   * WARNING: Since we're using a {@link HashMap} of {@link SSAInstruction}s, and equals() of {@link SSAInstruction} assumes a
   * canonical representative for each instruction, we must ensure that we use the same IR object throughout
   * initialization!!
   */
  private void populate() {
    if (!isPopulated) {
      // ensure that we keep the single, canonical IR live throughout initialization, while the instructionIndices map
      // is live.
      IR ir = node.getIR();
      isPopulated = true;

      Map instructionIndices = computeInstructionIndices(ir);
      createNodes(ref, cOptions, ir);
      createScalarEdges(cOptions, ir, instructionIndices);
    }
  }

  private void createScalarEdges(ControlDependenceOptions cOptions, IR ir, Map instructionIndices) {
    createScalarDataDependenceEdges(ir, instructionIndices);
    createControlDependenceEdges(cOptions, ir, instructionIndices);
  }

  /**
   * return the set of all PARAM_CALLER and HEAP_PARAM_CALLER statements associated with a given call
   */
  public Set getCallerParamStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException {
    if (call == null) {
      throw new IllegalArgumentException("call == null");
    }
    populate();
    return callerParamStatements.get(call.getCallSite());
  }

  /**
   * return the set of all PARAM_CALLER, HEAP_PARAM_CALLER, and NORMAL statements (i.e., the actual call statement) associated with
   * a given call
   */
  public Set getCallStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException {
    Set callerParamStatements = getCallerParamStatements(call);
    Set result = HashSetFactory.make(callerParamStatements.size() + 1);
    result.addAll(callerParamStatements);
    result.add(callSite2Statement.get(call.getCallSite()));
    return result;
  }

  /**
   * return the set of all NORMAL_RETURN_CALLER and HEAP_RETURN_CALLER statements associated with a given call.
   */
  public Set getCallerReturnStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException {
    if (call == null) {
      throw new IllegalArgumentException("call == null");
    }
    populate();
    return callerReturnStatements.get(call.getCallSite());
  }

  /**
   * Create all control dependence edges in this PDG.
   */
  private void createControlDependenceEdges(ControlDependenceOptions cOptions, IR ir,
      Map instructionIndices) {
    if (cOptions.equals(ControlDependenceOptions.NONE)) {
      return;
    }
    if (ir == null) {
      return;
    }
    ControlFlowGraph controlFlowGraph = ir.getControlFlowGraph();
    if (cOptions.equals(ControlDependenceOptions.NO_EXCEPTIONAL_EDGES)) {
      controlFlowGraph = ExceptionPrunedCFG.make(controlFlowGraph);
    if (!dOptions.isIgnoreExceptions()) {
      // In case the CFG has no nodes left because the only control dependencies
      // were
      // exceptional, simply return because at this point there are no nodes.
      // Otherwise, later this may raise an Exception.
      if (controlFlowGraph.getNumberOfNodes() == 0) {
        return;
      }
    } else {
      Assertions.productionAssertion(cOptions.equals(ControlDependenceOptions.FULL));
    }

    ControlDependenceGraph cdg = new ControlDependenceGraph(
        controlFlowGraph);
    for (ISSABasicBlock bb : cdg) {
      if (bb.isExitBlock()) {
        // nothing should be control-dependent on the exit block.
        continue;
      }

      Statement src = null;
      if (bb.isEntryBlock()) {
        src = new MethodEntryStatement(node);
      } else {
        SSAInstruction s = ir.getInstructions()[bb.getLastInstructionIndex()];
        if (s == null) {
          // should have no control dependent successors.
          // leave src null.
        } else {
          src = ssaInstruction2Statement(s, ir, instructionIndices);
          // add edges from call statements to parameter passing and return
          // SJF: Alexey and I think that we should just define ParamStatements
          // as
          // being control dependent on nothing ... they only represent pure
          // data dependence. So, I'm commenting out the following.
          // if (s instanceof SSAAbstractInvokeInstruction) {
          // SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction)
          // s;
          // for (Statement st : callerParamStatements.get(call.getCallSite()))
          // {
          // addEdge(src, st);
          // }
          // for (Statement st : callerReturnStatements.get(call.getCallSite()))
          // {
          // addEdge(src, st);
          // }
          // }
        }
      }
      // add edges for every control-dependent statement in the IR, if there are
      // any
      // control-dependent successors
      if (src != null) {
        for (Iterator succ = cdg.getSuccNodes(bb); succ.hasNext();) {
          ISSABasicBlock bb2 = succ.next();
          for (Iterator it2 = bb2.iterator(); it2.hasNext();) {
            SSAInstruction st = it2.next();
            if (st != null) {
              Statement dest = ssaInstruction2Statement(st, ir, instructionIndices);
              assert src != null;
              delegate.addEdge(src, dest);
/** BEGIN Custom change: control deps */                
              delegate.addEdge(src, dest, Dependency.CONTROL_DEP);
/** END Custom change: control deps */                
            }
          }
        }
      }
    }

    // the CDG does not represent control dependences from the entry node.
    // add these manually
    // We add control dependences to all instructions in all basic blocks B that _must_ execute.
    // B is the set of blocks that dominate the exit basic block
    Statement methodEntry = new MethodEntryStatement(node);
    Dominators dom = Dominators.make(controlFlowGraph, controlFlowGraph.entry());
    for (ISSABasicBlock exitDom : Iterator2Iterable.make(dom.dominators(controlFlowGraph.exit()))) {
      for (SSAInstruction st : exitDom) {
        Statement dest = ssaInstruction2Statement(st, ir, instructionIndices);
        delegate.addEdge(methodEntry, dest);
/** BEGIN Custom change: control deps */                
        delegate.addEdge(methodEntry, dest, Dependency.CONTROL_DEP);
/** END Custom change: control deps */                
      }
    }
    // add CD from method entry to all callee parameter assignments
    // SJF: Alexey and I think that we should just define ParamStatements as
    // being control dependent on nothing ... they only represent pure
    // data dependence. So, I'm commenting out the following.
    @Override
    // for (int i = 0; i < paramCalleeStatements.length; i++) {
    // addEdge(methodEntry, paramCalleeStatements[i]);
    // }

    /**
     * JTD: While phi nodes live in a particular basic block, they represent a meet of values from multiple blocks. Hence, they are
     * really like multiple statements that are control dependent in the manner of the predecessor blocks. When the slicer is
     * following both data and control dependences, it therefore seems right to add control dependence edges to represent how a phi
     * node depends on predecessor blocks.
     */
    if (!dOptions.equals(DataDependenceOptions.NONE)) {
      for (ISSABasicBlock bb : cdg) {
        for (Iterator ps = bb.iteratePhis(); ps.hasNext();) {
          SSAPhiInstruction phi = ps.next();
          Statement phiSt = ssaInstruction2Statement(phi, ir, instructionIndices);
          int phiUseIndex = 0;
          for (Iterator preds = controlFlowGraph.getPredNodes(bb); preds.hasNext();) {
            ISSABasicBlock pb = preds.next();
            int use = phi.getUse(phiUseIndex);
            if (use == AbstractIntStackMachine.TOP) {
              // the predecessor is part of some infeasible bytecode. we probably don't want slices to include such code, so ignore.
              continue;
            }
            if (controlFlowGraph.getSuccNodeCount(pb) > 1) {
              // in this case, there is more than one edge from the
              // predecessor block, hence the phi node actually
              // depends on the last instruction in the previous
              // block, rather than having the same dependences as
              // statements in that block.
              SSAInstruction pss = ir.getInstructions()[pb.getLastInstructionIndex()];
              assert pss != null;
              Statement pst = ssaInstruction2Statement(pss, ir, instructionIndices);
              delegate.addEdge(pst, phiSt);
/** BEGIN Custom change: control deps */                
              delegate.addEdge(pst, phiSt, Dependency.CONTROL_DEP);
/** END Custom change: control deps */                
            } else {
              for (Iterator cdps = cdg.getPredNodes(pb); cdps.hasNext();) {
                ISSABasicBlock cpb = cdps.next();
/** BEGIN Custom change: control deps */                
                if (cpb.getLastInstructionIndex() < 0) {
                  continue;
                }
/** END Custom change: control deps */                
                SSAInstruction cps = ir.getInstructions()[cpb.getLastInstructionIndex()];
                assert cps != null : "unexpected null final instruction for CDG predecessor " + cpb + " in node " + node;
                Statement cpst = ssaInstruction2Statement(cps, ir, instructionIndices);
                delegate.addEdge(cpst, phiSt);
/** BEGIN Custom change: control deps */                
                delegate.addEdge(cpst, phiSt, Dependency.CONTROL_DEP);
/** END Custom change: control deps */                
              }
            }
            phiUseIndex++;
          }
        }
      }
    }
  }

  /**
   * Create all data dependence edges in this PDG.
   * 
   * Scalar dependences are taken from SSA def-use information.
   * 
   * Heap dependences are computed by a reaching defs analysis.
   * 
   * @param pa
   * @param mod
   */
  private void createScalarDataDependenceEdges(IR ir, Map instructionIndices) {
    if (dOptions.equals(DataDependenceOptions.NONE)) {
      return;

    if (ir == null) {
      return;
    }

    // this is tricky .. I'm explicitly creating a new DefUse to make sure it refers to the instructions we need from
    // the "one true" ir of the moment.
    DefUse DU = new DefUse(ir);
    SSAInstruction[] instructions = ir.getInstructions();

    //
    // TODO: teach some other bit of code about the uses of
    // GetCaughtException, and then delete this code.
    //
    populate();
      for (ISSABasicBlock bb : ir.getControlFlowGraph()) {
        if (bb.isCatchBlock()) {
          SSACFG.ExceptionHandlerBasicBlock ehbb = (SSACFG.ExceptionHandlerBasicBlock) bb;

          if (ehbb.getCatchInstruction() != null) {
            Statement c = ssaInstruction2Statement(ehbb.getCatchInstruction(), ir, instructionIndices);

            for (ISSABasicBlock pb : ir.getControlFlowGraph().getExceptionalPredecessors(ehbb)) {
              SSAInstruction st = instructions[pb.getLastInstructionIndex()];

              if (st instanceof SSAAbstractInvokeInstruction) {
                delegate.addEdge(new ExceptionalReturnCaller(node, pb.getLastInstructionIndex()), c);
              } else if (st instanceof SSAAbstractThrowInstruction) {
                delegate.addEdge(ssaInstruction2Statement(st, ir, instructionIndices), c);
              }
            }
          }
        }
      }
    }

    for (Iterator it = iterator(); it.hasNext();) {
      Statement s = it.next();
      switch (s.getKind()) {
      case NORMAL:
      case CATCH:
      case PI:
      case PHI: {
        SSAInstruction statement = statement2SSAInstruction(instructions, s);
        // note that data dependencies from invoke instructions will pass
        // interprocedurally
        if (!(statement instanceof SSAAbstractInvokeInstruction)) {
          if (dOptions.isTerminateAtCast() && (statement instanceof SSACheckCastInstruction)) {
            break;
          }
          if (dOptions.isTerminateAtCast() && (statement instanceof SSAInstanceofInstruction)) {
            break;
          }
          // add edges from this statement to every use of the def of this
          // statement
          for (int i = 0; i < statement.getNumberOfDefs(); i++) {
            int def = statement.getDef(i);
            for (Iterator it2 = DU.getUses(def); it2.hasNext();) {
              SSAInstruction use = it2.next();
              if (dOptions.isIgnoreBasePtrs()) {
                if (use instanceof SSANewInstruction) {
                  // cut out array length parameters
                  continue;
                }
                if (hasBasePointer(use)) {
                  int base = getBasePointer(use);
                  if (def == base) {
                    // skip the edge to the base pointer
                    continue;
                  }
                  if (use instanceof SSAArrayReferenceInstruction) {
                    SSAArrayReferenceInstruction arr = (SSAArrayReferenceInstruction) use;
                    if (def == arr.getIndex()) {
                      // skip the edge to the array index
                      continue;
                    }
                  }
                }
              }
              Statement u = ssaInstruction2Statement(use, ir, instructionIndices);
              delegate.addEdge(s, u);
            }
          }
        }
        break;
      }
      case EXC_RET_CALLER:
      case NORMAL_RET_CALLER:
      case PARAM_CALLEE: {
        if (dOptions.isIgnoreExceptions()) {
          assert !s.getKind().equals(Kind.EXC_RET_CALLER);
        }

        ValueNumberCarrier a = (ValueNumberCarrier) s;
        for (Iterator it2 = DU.getUses(a.getValueNumber()); it2.hasNext();) {
          SSAInstruction use = it2.next();
          if (dOptions.isIgnoreBasePtrs()) {
            if (use instanceof SSANewInstruction) {
              // cut out array length parameters
              continue;
            }
            if (hasBasePointer(use)) {
              int base = getBasePointer(use);
              if (a.getValueNumber() == base) {
                // skip the edge to the base pointer
                continue;
              }
              if (use instanceof SSAArrayReferenceInstruction) {
                SSAArrayReferenceInstruction arr = (SSAArrayReferenceInstruction) use;
                if (a.getValueNumber() == arr.getIndex()) {
                  // skip the edge to the array index
                  continue;
                }
              }
            }
          }
          Statement u = ssaInstruction2Statement(use, ir, instructionIndices);
          delegate.addEdge(s, u);
        }
        break;
      }
      case NORMAL_RET_CALLEE:
        for (NormalStatement ret : computeReturnStatements(ir)) {
          delegate.addEdge(ret, s);
        }
        break;
      case EXC_RET_CALLEE:
        if (dOptions.isIgnoreExceptions()) {
          Assertions.UNREACHABLE();
        }
        // TODO: this is overly conservative. deal with catch blocks?
        for (IntIterator ii = getPEIs(ir).intIterator(); ii.hasNext();) {
          int index = ii.next();
          SSAInstruction pei = ir.getInstructions()[index];
          if (dOptions.isTerminateAtCast() && (pei instanceof SSACheckCastInstruction)) {
            continue;
          }
          if (pei instanceof SSAAbstractInvokeInstruction) {
            Statement st = new ExceptionalReturnCaller(node, index);
            delegate.addEdge(st, s);
          } else {
            delegate.addEdge(new NormalStatement(node, index), s);
          }
        }
        break;
      case PARAM_CALLER: {
        ParamCaller pac = (ParamCaller) s;
        int vn = pac.getValueNumber();
        // note that if the caller is the fake root method and the parameter
        // type is primitive,
        // it's possible to have a value number of -1. If so, just ignore it.
        if (vn > -1) {
          if (ir.getSymbolTable().isParameter(vn)) {
            Statement a = new ParamCallee(node, vn);
            delegate.addEdge(a, pac);
          } else {
            SSAInstruction d = DU.getDef(vn);
            if (dOptions.isTerminateAtCast() && (d instanceof SSACheckCastInstruction)) {
              break;
            }
            if (d != null) {
              if (d instanceof SSAAbstractInvokeInstruction) {
                SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction) d;
                if (vn == call.getException()) {
                  Statement st = new ExceptionalReturnCaller(node, instructionIndices.get(d));
                  delegate.addEdge(st, pac);
                } else {
                  Statement st = new NormalReturnCaller(node, instructionIndices.get(d));
                  delegate.addEdge(st, pac);
                }
              } else {
                Statement ds = ssaInstruction2Statement(d, ir, instructionIndices);
                delegate.addEdge(ds, pac);
              }
            }
          }
        }
      }
        break;

      case HEAP_RET_CALLEE:
      case HEAP_RET_CALLER:
      case HEAP_PARAM_CALLER:
      case HEAP_PARAM_CALLEE:
      case METHOD_ENTRY:
      case METHOD_EXIT:
        // do nothing
        break;
      default:
        Assertions.UNREACHABLE(s.toString());
        break;
      }
    }
  }

  private static class SingletonSet extends SetOfClasses implements Serializable {

    /* Serial version */
    private static final long serialVersionUID = -3256390509887654324L;

    private final TypeReference t;

    SingletonSet(TypeReference t) {
      this.t = t;
    }

    public void add(IClass klass) {
      Assertions.UNREACHABLE();
    }

    @Override
    public boolean contains(String klassName) {
      Assertions.UNREACHABLE();
      return false;
    }

    @Override
    public boolean contains(TypeReference klass) {
      return t.equals(klass);
    }
  }

  private static class SetComplement extends SetOfClasses implements Serializable {

    /* Serial version */
    private static final long serialVersionUID = -3256390509887654323L;

    private final SetOfClasses set;

    SetComplement(SetOfClasses set) {
      this.set = set;
    }

    static SetComplement complement(SetOfClasses set) {
      return new SetComplement(set);
    }

    @Override
    public void add(IClass klass) {
      Assertions.UNREACHABLE();
    }

    @Override
    public boolean contains(String klassName) {
      Assertions.UNREACHABLE();
      return false;
    }

    @Override
    public boolean contains(TypeReference klass) {
      return !set.contains(klass);
    }
  }

  /**
   * Create heap data dependence edges in this PDG relevant to a particular {@link PointerKey}.
   */
  private void createHeapDataDependenceEdges(final PointerKey pk) {

    if (locationsHandled.contains(pk)) {
      return;
    } else {
      locationsHandled.add(pk);
    }
    if (dOptions.isIgnoreHeap() || (exclusions != null && exclusions.excludes(pk))) {
      return;
    }

    TypeReference t = HeapExclusions.getType(pk);
    if (t == null) {
      return;
    }

    // It's OK to create a new IR here; we're not keeping any hashing live up to this point
    IR ir = node.getIR();
    if (ir == null) {
      return;
    }

    if (VERBOSE) {
      System.err.println("Location " + pk);
    }

    // in reaching defs calculation, exclude heap statements that are
    // irrelevant.
    Filter f = new Filter() {
      public boolean accepts(Object o) {
        if (o instanceof HeapStatement) {
          HeapStatement h = (HeapStatement) o;
          return h.getLocation().equals(pk);
        } else {
          return true;
        }
      }
    };
    Collection relevantStatements = Iterator2Collection.toSet(new FilterIterator(iterator(), f));

    Map> heapReachingDefs = new HeapReachingDefs(modRef).computeReachingDefs(node, ir, pa, mod,
        relevantStatements, new HeapExclusions(SetComplement.complement(new SingletonSet(t))), cg);

    for (Statement st : heapReachingDefs.keySet()) {
      switch (st.getKind()) {
      case NORMAL:
      case CATCH:
      case PHI:
      case PI: {
        OrdinalSet defs = heapReachingDefs.get(st);
        if (defs != null) {
          for (Statement def : defs) {
            delegate.addEdge(def, st);
          }
        }
      }
        break;
      case EXC_RET_CALLER:
      case NORMAL_RET_CALLER:
      case PARAM_CALLEE:
      case NORMAL_RET_CALLEE:
      case PARAM_CALLER:
      case EXC_RET_CALLEE:
        break;
      case HEAP_RET_CALLEE:
      case HEAP_RET_CALLER:
      case HEAP_PARAM_CALLER: {
        OrdinalSet defs = heapReachingDefs.get(st);
        if (defs != null) {
          for (Statement def : defs) {
            delegate.addEdge(def, st);
          }
        }
        break;
      }
      case HEAP_PARAM_CALLEE:
      case METHOD_ENTRY:
      case METHOD_EXIT:
        // do nothing .. there are no incoming edges
        break;
      default:
        Assertions.UNREACHABLE(st.toString());
        break;
      }
    }
  }

  private boolean hasBasePointer(SSAInstruction use) {
    if (use instanceof SSAFieldAccessInstruction) {
      SSAFieldAccessInstruction f = (SSAFieldAccessInstruction) use;
      return !f.isStatic();
    } else if (use instanceof SSAArrayReferenceInstruction) {
      return true;
    } else if (use instanceof SSAArrayLengthInstruction) {
      return true;
    } else {
      return false;
    }
  }

  private int getBasePointer(SSAInstruction use) {
    if (use instanceof SSAFieldAccessInstruction) {
      SSAFieldAccessInstruction f = (SSAFieldAccessInstruction) use;
      return f.getRef();
    } else if (use instanceof SSAArrayReferenceInstruction) {
      SSAArrayReferenceInstruction a = (SSAArrayReferenceInstruction) use;
      return a.getArrayRef();
    } else if (use instanceof SSAArrayLengthInstruction) {
      SSAArrayLengthInstruction s = (SSAArrayLengthInstruction) use;
      return s.getArrayRef();
    } else {
      Assertions.UNREACHABLE("BOOM");
      return -1;
    }
  }

  /**
   * @return Statements representing each return instruction in the ir
   */
  private Collection computeReturnStatements(final IR ir) {
    Filter filter = new Filter() {
      public boolean accepts(Object o) {
        if (o instanceof NormalStatement) {
          NormalStatement s = (NormalStatement) o;
          SSAInstruction st = ir.getInstructions()[s.getInstructionIndex()];
          return st instanceof SSAReturnInstruction;
        } else {
          return false;
        }
      }
    };
    return Iterator2Collection.toSet(new FilterIterator(iterator(), filter));
  }

  /**
   * @return {@link IntSet} representing instruction indices of each PEI in the ir
   */
  private IntSet getPEIs(final IR ir) {
    BitVectorIntSet result = new BitVectorIntSet();
    for (int i = 0; i < ir.getInstructions().length; i++) {
      if (ir.getInstructions()[i] != null && ir.getInstructions()[i].isPEI()) {
        result.add(i);
      }
    }
    return result;
  }

  /**
   *          SDG.
   * Wrap an {@link SSAInstruction} in a {@link Statement}. WARNING: Since we're using a {@link HashMap} of {@link SSAInstruction}s,
   * and equals() of {@link SSAInstruction} assumes a canonical representative for each instruction, we must ensure that we
   * use the same IR object throughout initialization!!
   */
  private Statement ssaInstruction2Statement(SSAInstruction s, IR ir, Map instructionIndices) {
    return ssaInstruction2Statement(node, s, instructionIndices, ir);
  }

  public static synchronized Statement ssaInstruction2Statement(CGNode node, SSAInstruction s,
      Map instructionIndices, IR ir) {
    if (node == null) {
      throw new IllegalArgumentException("null node");
    }
    if (s == null) {
      throw new IllegalArgumentException("null s");
    }
    if (s instanceof SSAPhiInstruction) {
      SSAPhiInstruction phi = (SSAPhiInstruction) s;
      return new PhiStatement(node, phi);
    } else if (s instanceof SSAPiInstruction) {
      SSAPiInstruction pi = (SSAPiInstruction) s;
      return new PiStatement(node, pi);
    } else if (s instanceof SSAGetCaughtExceptionInstruction) {
      return new GetCaughtExceptionStatement(node, ((SSAGetCaughtExceptionInstruction) s));
    } else {
      Integer x = instructionIndices.get(s);
      if (x == null) {
        Assertions.UNREACHABLE(s.toString() + "\nnot found in map of\n" + ir);
      }
      return new NormalStatement(node, x.intValue());
    }
  }

  /**
   * @return for each SSAInstruction, its instruction index in the ir instruction array
   */
  public static Map computeInstructionIndices(IR ir) {
    Map result = HashMapFactory.make();
    if (ir != null) {
      SSAInstruction[] instructions = ir.getInstructions();
      for (int i = 0; i < instructions.length; i++) {
        SSAInstruction s = instructions[i];
        if (s != null) {
          result.put(s, new Integer(i));
        }
      }
    }
    return result;
  }

  /**
   * Convert a NORMAL or PHI Statement to an SSAInstruction
   */
  private SSAInstruction statement2SSAInstruction(SSAInstruction[] instructions, Statement s) {
    SSAInstruction statement = null;
    switch (s.getKind()) {
    case NORMAL:
      NormalStatement n = (NormalStatement) s;
      statement = instructions[n.getInstructionIndex()];
      break;
    case PHI:
      PhiStatement p = (PhiStatement) s;
      statement = p.getPhi();
      break;
    case PI:
      PiStatement ps = (PiStatement) s;
      statement = ps.getPi();
      break;
    case CATCH:
      GetCaughtExceptionStatement g = (GetCaughtExceptionStatement) s;
      statement = g.getInstruction();
      break;
    default:
      Assertions.UNREACHABLE(s.toString());
    }
    return statement;
  }

  /**
   * Create all nodes in this PDG. Each node is a Statement.
   */
  private void createNodes(Map> ref, ControlDependenceOptions cOptions, IR ir) {

    if (ir != null) {
      createNormalStatements(ir, ref);
      createSpecialStatements(ir);
    }

    createCalleeParams();
    createReturnStatements();

    delegate.addNode(new MethodEntryStatement(node));
    delegate.addNode(new MethodExitStatement(node));
  }

  /**
   * create nodes representing defs of the return values
   * 
   * @param mod the set of heap locations which may be written (transitively) by this node. These are logically parameters in the
   * @param dOptions
   */
  private void createReturnStatements() {
    ArrayList list = new ArrayList();
    if (!node.getMethod().getReturnType().equals(TypeReference.Void)) {
      NormalReturnCallee n = new NormalReturnCallee(node);
      delegate.addNode(n);
      list.add(n);
    }
    if (!dOptions.isIgnoreExceptions()) {
      ExceptionalReturnCallee e = new ExceptionalReturnCallee(node);
      delegate.addNode(e);
      list.add(e);
    }
    if (!dOptions.isIgnoreHeap()) {
      for (PointerKey p : mod.get(node)) {
        Statement h = new HeapStatement.HeapReturnCallee(node, p);
        delegate.addNode(h);
        list.add(h);
      }
    }
    returnStatements = new Statement[list.size()];
    list.toArray(returnStatements);
  }

  /**
   * create nodes representing defs of formal parameters
   * 
   * @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG.
   */
  private void createCalleeParams() {
    if (paramCalleeStatements == null) {
      ArrayList list = new ArrayList();
      for (int i = 1; i <= node.getMethod().getNumberOfParameters(); i++) {
        ParamCallee s = new ParamCallee(node, i);
        delegate.addNode(s);
        list.add(s);
      }
      if (!dOptions.isIgnoreHeap()) {
        for (PointerKey p : ref.get(node)) {
          Statement h = new HeapStatement.HeapParamCallee(node, p);
          delegate.addNode(h);
          list.add(h);
        }
      }
      paramCalleeStatements = new Statement[list.size()];
      list.toArray(paramCalleeStatements);
    }
  }

  /**
   * Create nodes corresponding to
   * 
    *
  • phi instructions *
  • getCaughtExceptions *
*/ private void createSpecialStatements(IR ir) { // create a node for instructions which do not correspond to bytecode for (Iterator it = ir.iterateAllInstructions(); it.hasNext();) { SSAInstruction s = it.next(); if (s instanceof SSAPhiInstruction) { delegate.addNode(new PhiStatement(node, (SSAPhiInstruction) s)); } else if (s instanceof SSAGetCaughtExceptionInstruction) { delegate.addNode(new GetCaughtExceptionStatement(node, (SSAGetCaughtExceptionInstruction) s)); } else if (s instanceof SSAPiInstruction) { delegate.addNode(new PiStatement(node, (SSAPiInstruction) s)); } } } /** * Create nodes in the graph corresponding to "normal" (bytecode) instructions */ private void createNormalStatements(IR ir, Map> ref) { // create a node for every normal instruction in the IR SSAInstruction[] instructions = ir.getInstructions(); for (int i = 0; i < instructions.length; i++) { SSAInstruction s = instructions[i]; if (s instanceof SSAGetCaughtExceptionInstruction) { continue; } if (s != null) { final NormalStatement statement = new NormalStatement(node, i); delegate.addNode(statement); if (s instanceof SSAAbstractInvokeInstruction) { callSite2Statement.put(((SSAAbstractInvokeInstruction) s).getCallSite(), statement); addParamPassingStatements(i, ref, ir); } } } } /** * Create nodes in the graph corresponding to in/out parameter passing for a call instruction */ populate(); private void addParamPassingStatements(int callIndex, Map> ref, IR ir) { SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction) ir.getInstructions()[callIndex]; Collection params = MapUtil.findOrCreateSet(callerParamStatements, call.getCallSite()); Collection rets = MapUtil.findOrCreateSet(callerReturnStatements, call.getCallSite()); for (int j = 0; j < call.getNumberOfUses(); j++) { Statement st = new ParamCaller(node, callIndex, call.getUse(j)); delegate.addNode(st); params.add(st); } if (!call.getDeclaredResultType().equals(TypeReference.Void)) { Statement st = new NormalReturnCaller(node, callIndex); delegate.addNode(st); rets.add(st); } { if (!dOptions.isIgnoreExceptions()) { Statement st = new ExceptionalReturnCaller(node, callIndex); delegate.addNode(st); rets.add(st); } } if (!dOptions.isIgnoreHeap()) { OrdinalSet uref = unionHeapLocations(cg, node, call, ref); for (PointerKey p : uref) { Statement st = new HeapStatement.HeapParamCaller(node, callIndex, p); delegate.addNode(st); params.add(st); } OrdinalSet umod = unionHeapLocations(cg, node, call, mod); for (PointerKey p : umod) { Statement st = new HeapStatement.HeapReturnCaller(node, callIndex, p); delegate.addNode(st); rets.add(st); } } } /** * @return the set of all locations read by any callee at a call site. */ private OrdinalSet unionHeapLocations(CallGraph cg, CGNode n, SSAAbstractInvokeInstruction call, Map> loc) { BitVectorIntSet bv = new BitVectorIntSet(); for (CGNode t : cg.getPossibleTargets(n, call.getCallSite())) { bv.addAll(loc.get(t).getBackingSet()); } return new OrdinalSet(bv, loc.get(n).getMapping()); } @Override public String toString() { populate(); StringBuffer result = new StringBuffer("PDG for " + node + ":\n"); result.append(super.toString()); return result.toString(); } public Statement[] getParamCalleeStatements() { if (paramCalleeStatements == null) { createCalleeParams(); } Statement[] result = new Statement[paramCalleeStatements.length]; System.arraycopy(paramCalleeStatements, 0, result, 0, result.length); return result; } public Statement[] getReturnStatements() { populate(); Statement[] result = new Statement[returnStatements.length]; System.arraycopy(returnStatements, 0, result, 0, result.length); return result; } public CGNode getCallGraphNode() { return node; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass().equals(obj.getClass())) { return node.equals(((PDG) obj).node); } else { return false; } } @Override public int hashCode() { return 103 * node.hashCode(); } public int getPredNodeCount(Statement N) throws UnimplementedError { populate(); Assertions.UNREACHABLE(); return delegate.getPredNodeCount(N); } public Iterator getPredNodes(Statement N) { if (!dOptions.isIgnoreHeap()) { computeIncomingHeapDependencies(N); } return delegate.getPredNodes(N); } private void computeIncomingHeapDependencies(Statement N) { switch (N.getKind()) { case NORMAL: NormalStatement st = (NormalStatement) N; if (!(ignoreAllocHeapDefs && st.getInstruction() instanceof SSANewInstruction)) { Collection ref = modRef.getRef(node, heapModel, pa, st.getInstruction(), exclusions); for (PointerKey pk : ref) { createHeapDataDependenceEdges(pk); } } break; case HEAP_PARAM_CALLEE: case HEAP_PARAM_CALLER: case HEAP_RET_CALLEE: case HEAP_RET_CALLER: HeapStatement h = (HeapStatement) N; createHeapDataDependenceEdges(h.getLocation()); } } private void computeOutgoingHeapDependencies(Statement N) { switch (N.getKind()) { case NORMAL: NormalStatement st = (NormalStatement) N; if (!(ignoreAllocHeapDefs && st.getInstruction() instanceof SSANewInstruction)) { Collection mod = modRef.getMod(node, heapModel, pa, st.getInstruction(), exclusions); for (PointerKey pk : mod) { createHeapDataDependenceEdges(pk); } } break; case HEAP_PARAM_CALLEE: case HEAP_PARAM_CALLER: case HEAP_RET_CALLEE: case HEAP_RET_CALLER: HeapStatement h = (HeapStatement) N; createHeapDataDependenceEdges(h.getLocation()); } } public int getSuccNodeCount(Statement N) throws UnimplementedError { populate(); Assertions.UNREACHABLE(); return delegate.getSuccNodeCount(N); } public Iterator getSuccNodes(Statement N) { populate(); if (!dOptions.isIgnoreHeap()) { computeOutgoingHeapDependencies(N); } return delegate.getSuccNodes(N); } public boolean hasEdge(Statement src, Statement dst) throws UnimplementedError { populate(); return delegate.hasEdge(src, dst); } public void removeNodeAndEdges(Statement N) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public void addNode(Statement n) { Assertions.UNREACHABLE(); } public boolean containsNode(Statement N) { populate(); return delegate.containsNode(N); } public int getNumberOfNodes() { populate(); return delegate.getNumberOfNodes(); } public Iterator iterator() { populate(); return delegate.iterator(); } public void removeNode(Statement n) { Assertions.UNREACHABLE(); } public void addEdge(Statement src, Statement dst) { Assertions.UNREACHABLE(); } public void removeAllIncidentEdges(Statement node) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public void removeEdge(Statement src, Statement dst) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public void removeIncomingEdges(Statement node) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public void removeOutgoingEdges(Statement node) throws UnsupportedOperationException { Assertions.UNREACHABLE(); } public int getMaxNumber() { populate(); return delegate.getMaxNumber(); } public Statement getNode(int number) { populate(); return delegate.getNode(number); } public int getNumber(Statement N) { return delegate.getNumber(N); } public Iterator iterateNodes(IntSet s) { Assertions.UNREACHABLE(); return null; } public IntSet getPredNodeNumbers(Statement node) { Assertions.UNREACHABLE(); return null; } public IntSet getSuccNodeNumbers(Statement node) { Assertions.UNREACHABLE(); return null; } /** BEGIN Custom change: control deps */ public boolean isControlDependend(Statement from, Statement to) { return delegate.hasEdge(from, to, Dependency.CONTROL_DEP); } /** END Custom change: control deps */ }
File
PDG.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.summaries;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.classLoader.CallSiteReference;
    if (DEBUG) {
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
import com.ibm.wala.ipa.callgraph.impl.ClassHierarchyMethodTargetSelector;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.types.MemberReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.strings.Atom;

/**
 * "Non-standard" bypass rules to use during call graph construction.
 * 
 * Normally, the method bypass rules replace the IMethod that is resolved by other means, via the getBypass() method. However, the
 * bypass rules can be invoked even before resolving the target of a call, by checking the intercept rules.
 * 
 * @author sfink
 */
public class BypassMethodTargetSelector implements MethodTargetSelector {

  static final boolean DEBUG = false;

  /**
   * Method summaries collected for methods. Mapping Object -> MethodSummary where Object is either a
   * 
    *
  • MethodReference *
  • TypeReference *
  • Atom (package name) *
*/ private final Map methodSummaries; /** * Set of Atoms representing package names whose methods should be treated as no-ops */ private final Set ignoredPackages; /** * Governing class hierarchy. */ protected final IClassHierarchy cha; /** * target selector to use for non-bypassed calls */ protected final MethodTargetSelector parent; /** * for checking method target resolution via CHA */ private final ClassHierarchyMethodTargetSelector chaMethodTargetSelector; /** * Mapping from MethodReference -> SyntheticMethod We may call syntheticMethod.put(m,null) .. in which case we use containsKey() * to check for having already considered m. */ final private HashMap syntheticMethods = HashMapFactory.make(); /** * @param parent * @param methodSummaries * @param ignoredPackages * @param cha */ public BypassMethodTargetSelector(MethodTargetSelector parent, Map methodSummaries, Set ignoredPackages, IClassHierarchy cha) { this.methodSummaries = methodSummaries; this.ignoredPackages = ignoredPackages; this.parent = parent; this.cha = cha; this.chaMethodTargetSelector = new ClassHierarchyMethodTargetSelector(cha); } /** * Check to see if a particular call site should be bypassed, before checking normal resolution of the receiver. * * @throws IllegalArgumentException if site is null */ public IMethod getCalleeTarget(CGNode caller, CallSiteReference site, IClass dispatchType) { if (site == null) { throw new IllegalArgumentException("site is null"); } // first, see if we'd like to bypass the CHA-based target for the site MethodReference ref = site.getDeclaredTarget(); IMethod chaTarget = chaMethodTargetSelector.getCalleeTarget(caller, site, dispatchType); IMethod target = (chaTarget == null) ? findOrCreateSyntheticMethod(ref, site.isStatic()) : findOrCreateSyntheticMethod(chaTarget, site.isStatic()); System.err.println("target is initially " + target); } if (target != null) { return target; } else { // didn't bypass the CHA target; check if we should bypass the parent target if (canIgnore(site.getDeclaredTarget())) { // we want to generate a NoOpSummary for this method. return findOrCreateSyntheticMethod(site.getDeclaredTarget(), site.isStatic()); } if (parent instanceof ClassHierarchyMethodTargetSelector) { // already checked this case and decided not to bypass return chaTarget; } target = parent.getCalleeTarget(caller, site, dispatchType); if (DEBUG) { System.err.println("target becomes " + target); } if (target != null) { IMethod bypassTarget = findOrCreateSyntheticMethod(target, site.isStatic()); if (DEBUG) System.err.println("bypassTarget is " + target); return (bypassTarget == null) ? target : bypassTarget; } else return target; } } /** * @param m a method reference * @return a SyntheticMethod corresponding to m; or null if none is available. */ protected SyntheticMethod findOrCreateSyntheticMethod(MethodReference m, boolean isStatic) { if (syntheticMethods.containsKey(m)) { return syntheticMethods.get(m); } else { MethodSummary summ = null; if (canIgnore(m)) { TypeReference T = m.getDeclaringClass(); IClass C = cha.lookupClass(T); if (C == null) { // did not load class; don't try to create a synthetic method syntheticMethods.put(m, null); return null; } summ = generateNoOp(m, isStatic); } else { summ = findSummary(m); } if (summ != null) { TypeReference T = m.getDeclaringClass(); IClass C = cha.lookupClass(T); if (C == null) { syntheticMethods.put(m, null); return null; } SummarizedMethod n = new SummarizedMethod(m, summ, C); syntheticMethods.put(m, n); return n; } else { syntheticMethods.put(m, null); return null; } } } /** * @param m a method reference * @return a SyntheticMethod corresponding to m; or null if none is available. */ protected SyntheticMethod findOrCreateSyntheticMethod(IMethod m, boolean isStatic) { MethodReference ref = m.getReference(); if (syntheticMethods.containsKey(ref)) { return syntheticMethods.get(ref); } else { MethodSummary summ = null; if (canIgnore(ref)) { summ = generateNoOp(ref, isStatic); } else { summ = findSummary(ref); } if (summ != null) { SummarizedMethod n = new SummarizedMethod(ref, summ, m.getDeclaringClass()); syntheticMethods.put(ref, n); return n; } else { syntheticMethods.put(ref, null); return null; } } } /** * Generate a {@link MethodSummary} which is the "standard" representation of a method * that does nothing. */ public static MethodSummary generateStandardNoOp(Language l, MethodReference m, boolean isStatic) { return new NoOpSummary(l, m, isStatic); } /** * Generate a {@link MethodSummary} which is the "standard" representation of a method * that does nothing. Subclasses may override this method to implement alternative semantics * concerning what "do nothing" means. */ public MethodSummary generateNoOp(MethodReference m, boolean isStatic) { Language l = cha.resolveMethod(m).getDeclaringClass().getClassLoader().getLanguage(); return new NoOpSummary(l, m, isStatic); } private static class NoOpSummary extends MethodSummary { private final Language l; public NoOpSummary(Language l, MethodReference method, boolean isStatic) { super(method); setStatic(isStatic); this.l = l; } /* * @see com.ibm.wala.ipa.summaries.MethodSummary#getStatements() */ @Override public SSAInstruction[] getStatements() { if (getReturnType().equals(TypeReference.Void)) { return NO_STATEMENTS; } else { int nullValue = getNumberOfParameters() + 1; SSAInstruction[] result = new SSAInstruction[1]; SSAInstructionFactory insts = l.instructionFactory(); result[0] = insts.ReturnInstruction(0, nullValue, getReturnType().isPrimitiveType()); return result; } } } /** * @param m * @return true iff we can treat m as a no-op method */ protected boolean canIgnore(MemberReference m) { TypeReference T = m.getDeclaringClass(); TypeName n = T.getName(); Atom p = n.getPackage(); return (ignoredPackages.contains(p)); } private MethodSummary findSummary(MemberReference m) { return methodSummaries.get(m); } protected IClassHierarchy getClassHierarchy() { return cha; } } ======= /******************************************************************************* * Copyright (c) 2002 - 2006 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.ipa.summaries; import java.util.HashMap; import java.util.Map; import java.util.Set; import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.classLoader.Language; import com.ibm.wala.classLoader.SyntheticMethod; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.MethodTargetSelector; import com.ibm.wala.ipa.callgraph.impl.ClassHierarchyMethodTargetSelector; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSAInstructionFactory; import com.ibm.wala.types.MemberReference; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeName; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.collections.HashMapFactory; import com.ibm.wala.util.strings.Atom; /** * "Non-standard" bypass rules to use during call graph construction. * * Normally, the method bypass rules replace the IMethod that is resolved by other means, via the getBypass() method. However, the /* * bypass rules can be invoked even before resolving the target of a call, by checking the intercept rules. * * @author sfink */ public class BypassMethodTargetSelector implements MethodTargetSelector { static final boolean DEBUG = false; /** * Method summaries collected for methods. Mapping Object -> MethodSummary where Object is either a *
    *
  • MethodReference *
  • TypeReference *
  • Atom (package name) *
*/ private final Map methodSummaries; /** * Set of Atoms representing package names whose methods should be treated as no-ops */ private final Set ignoredPackages; /** * Governing class hierarchy. */ protected final IClassHierarchy cha; /** * target selector to use for non-bypassed calls */ protected final MethodTargetSelector parent; /** * for checking method target resolution via CHA */ private final ClassHierarchyMethodTargetSelector chaMethodTargetSelector; /** * Mapping from MethodReference -> SyntheticMethod We may call syntheticMethod.put(m,null) .. in which case we use containsKey() * to check for having already considered m. */ final private HashMap syntheticMethods = HashMapFactory.make(); /** * @param parent * @param methodSummaries * @param ignoredPackages * @param cha */ public BypassMethodTargetSelector(MethodTargetSelector parent, Map methodSummaries, Set ignoredPackages, IClassHierarchy cha) { this.methodSummaries = methodSummaries; this.ignoredPackages = ignoredPackages; this.parent = parent; this.cha = cha; this.chaMethodTargetSelector = new ClassHierarchyMethodTargetSelector(cha); } /** * Check to see if a particular call site should be bypassed, before checking normal resolution of the receiver. * * @throws IllegalArgumentException if site is null */ public IMethod getCalleeTarget(CGNode caller, CallSiteReference site, IClass dispatchType) { if (site == null) { throw new IllegalArgumentException("site is null"); } // first, see if we'd like to bypass the CHA-based target for the site MethodReference ref = site.getDeclaredTarget(); IMethod chaTarget = chaMethodTargetSelector.getCalleeTarget(caller, site, dispatchType); IMethod target = (chaTarget == null) ? findOrCreateSyntheticMethod(ref, site.isStatic()) : findOrCreateSyntheticMethod(chaTarget, site.isStatic()); if (DEBUG) { System.err.println("target is initially " + target); } if (target != null) { return target; } else { // didn't bypass the CHA target; check if we should bypass the parent target if (canIgnore(site.getDeclaredTarget())) { // we want to generate a NoOpSummary for this method. return findOrCreateSyntheticMethod(site.getDeclaredTarget(), site.isStatic()); } if (parent instanceof ClassHierarchyMethodTargetSelector) { // already checked this case and decided not to bypass return chaTarget; } target = parent.getCalleeTarget(caller, site, dispatchType); if (DEBUG) { System.err.println("target becomes " + target); } if (target != null) { IMethod bypassTarget = findOrCreateSyntheticMethod(target, site.isStatic()); if (DEBUG) System.err.println("bypassTarget is " + target); return (bypassTarget == null) ? target : bypassTarget; } else return target; } } /** * @param m a method reference * @return a SyntheticMethod corresponding to m; or null if none is available. */ protected SyntheticMethod findOrCreateSyntheticMethod(MethodReference m, boolean isStatic) { if (syntheticMethods.containsKey(m)) { return syntheticMethods.get(m); } else { MethodSummary summ = null; if (canIgnore(m)) { TypeReference T = m.getDeclaringClass(); IClass C = cha.lookupClass(T); if (C == null) { // did not load class; don't try to create a synthetic method syntheticMethods.put(m, null); return null; } summ = generateNoOp(m, isStatic); } else { summ = findSummary(m); } if (summ != null) { TypeReference T = m.getDeclaringClass(); IClass C = cha.lookupClass(T); if (C == null) { syntheticMethods.put(m, null); return null; } SummarizedMethod n = new SummarizedMethod(m, summ, C); syntheticMethods.put(m, n); return n; } else { syntheticMethods.put(m, null); return null; } } } /** * @param m a method reference * @return a SyntheticMethod corresponding to m; or null if none is available. */ protected SyntheticMethod findOrCreateSyntheticMethod(IMethod m, boolean isStatic) { MethodReference ref = m.getReference(); if (syntheticMethods.containsKey(ref)) { return syntheticMethods.get(ref); } else { MethodSummary summ = null; if (canIgnore(ref)) { summ = generateNoOp(ref, isStatic); } else { summ = findSummary(ref); } if (summ != null) { SummarizedMethod n = new SummarizedMethod(ref, summ, m.getDeclaringClass()); syntheticMethods.put(ref, n); return n; } else { syntheticMethods.put(ref, null); return null; } } } /** * Generate a {@link MethodSummary} which is the "standard" representation of a method * that does nothing. */ public static MethodSummary generateStandardNoOp(Language l, MethodReference m, boolean isStatic) { return new NoOpSummary(l, m, isStatic); } /** * Generate a {@link MethodSummary} which is the "standard" representation of a method * that does nothing. Subclasses may override this method to implement alternative semantics * concerning what "do nothing" means. */ public MethodSummary generateNoOp(MethodReference m, boolean isStatic) { Language l = cha.resolveMethod(m).getDeclaringClass().getClassLoader().getLanguage(); return new NoOpSummary(l, m, isStatic); } private static class NoOpSummary extends MethodSummary { private final Language l; public NoOpSummary(Language l, MethodReference method, boolean isStatic) { super(method); setStatic(isStatic); this.l = l; } * @see com.ibm.wala.ipa.summaries.MethodSummary#getStatements() */ @Override public SSAInstruction[] getStatements() { if (getReturnType().equals(TypeReference.Void)) { return NO_STATEMENTS; } else { int nullValue = getNumberOfParameters() + 1; SSAInstruction[] result = new SSAInstruction[1]; SSAInstructionFactory insts = l.instructionFactory(); result[0] = insts.ReturnInstruction(nullValue, getReturnType().isPrimitiveType()); return result; } } } /** * @param m * @return true iff we can treat m as a no-op method */ protected boolean canIgnore(MemberReference m) { TypeReference T = m.getDeclaringClass(); TypeName n = T.getName(); Atom p = n.getPackage(); return (ignoredPackages.contains(p)); } private MethodSummary findSummary(MemberReference m) { return methodSummaries.get(m); } protected IClassHierarchy getClassHierarchy() { return cha; } } >>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.summaries;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.SyntheticMethod;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.MethodTargetSelector;
import com.ibm.wala.ipa.callgraph.impl.ClassHierarchyMethodTargetSelector;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.types.MemberReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.strings.Atom;

/**
 * "Non-standard" bypass rules to use during call graph construction.
 * 
 * Normally, the method bypass rules replace the IMethod that is resolved by other means, via the getBypass() method. However, the
 * bypass rules can be invoked even before resolving the target of a call, by checking the intercept rules.
 * 
 * @author sfink
 */
public class BypassMethodTargetSelector implements MethodTargetSelector {

  static final boolean DEBUG = false;

  /**
   * Method summaries collected for methods. Mapping Object -> MethodSummary where Object is either a
   * 
    *
  • MethodReference *
  • TypeReference *
  • Atom (package name) *
*/ private final Map methodSummaries; /** * Set of Atoms representing package names whose methods should be treated as no-ops */ private final Set ignoredPackages; /** * Governing class hierarchy. */ protected final IClassHierarchy cha; /** * target selector to use for non-bypassed calls */ protected final MethodTargetSelector parent; /** * for checking method target resolution via CHA */ private final ClassHierarchyMethodTargetSelector chaMethodTargetSelector; /** * Mapping from MethodReference -> SyntheticMethod We may call syntheticMethod.put(m,null) .. in which case we use containsKey() * to check for having already considered m. */ final private HashMap syntheticMethods = HashMapFactory.make(); /** * @param parent * @param methodSummaries * @param ignoredPackages * @param cha */ public BypassMethodTargetSelector(MethodTargetSelector parent, Map methodSummaries, Set ignoredPackages, IClassHierarchy cha) { this.methodSummaries = methodSummaries; this.ignoredPackages = ignoredPackages; this.parent = parent; this.cha = cha; this.chaMethodTargetSelector = new ClassHierarchyMethodTargetSelector(cha); } /** * Check to see if a particular call site should be bypassed, before checking normal resolution of the receiver. * * @throws IllegalArgumentException if site is null */ public IMethod getCalleeTarget(CGNode caller, CallSiteReference site, IClass dispatchType) { if (site == null) { throw new IllegalArgumentException("site is null"); } // first, see if we'd like to bypass the CHA-based target for the site MethodReference ref = site.getDeclaredTarget(); IMethod chaTarget = chaMethodTargetSelector.getCalleeTarget(caller, site, dispatchType); IMethod target = (chaTarget == null) ? findOrCreateSyntheticMethod(ref, site.isStatic()) : findOrCreateSyntheticMethod(chaTarget, site.isStatic()); if (DEBUG) { System.err.println("target is initially " + target); } if (target != null) { return target; } else { // didn't bypass the CHA target; check if we should bypass the parent target if (canIgnore(site.getDeclaredTarget())) { // we want to generate a NoOpSummary for this method. return findOrCreateSyntheticMethod(site.getDeclaredTarget(), site.isStatic()); } if (parent instanceof ClassHierarchyMethodTargetSelector) { // already checked this case and decided not to bypass return chaTarget; } target = parent.getCalleeTarget(caller, site, dispatchType); if (DEBUG) { System.err.println("target becomes " + target); } if (target != null) { IMethod bypassTarget = findOrCreateSyntheticMethod(target, site.isStatic()); if (DEBUG) System.err.println("bypassTarget is " + target); return (bypassTarget == null) ? target : bypassTarget; } else return target; } } /** * @param m a method reference * @return a SyntheticMethod corresponding to m; or null if none is available. */ protected SyntheticMethod findOrCreateSyntheticMethod(MethodReference m, boolean isStatic) { if (syntheticMethods.containsKey(m)) { return syntheticMethods.get(m); } else { MethodSummary summ = null; if (canIgnore(m)) { TypeReference T = m.getDeclaringClass(); IClass C = cha.lookupClass(T); if (C == null) { // did not load class; don't try to create a synthetic method syntheticMethods.put(m, null); return null; } summ = generateNoOp(m, isStatic); } else { summ = findSummary(m); } if (summ != null) { TypeReference T = m.getDeclaringClass(); IClass C = cha.lookupClass(T); if (C == null) { syntheticMethods.put(m, null); return null; } SummarizedMethod n = new SummarizedMethod(m, summ, C); syntheticMethods.put(m, n); return n; } else { syntheticMethods.put(m, null); return null; } } } /** * @param m a method reference * @return a SyntheticMethod corresponding to m; or null if none is available. */ protected SyntheticMethod findOrCreateSyntheticMethod(IMethod m, boolean isStatic) { MethodReference ref = m.getReference(); if (syntheticMethods.containsKey(ref)) { return syntheticMethods.get(ref); } else { MethodSummary summ = null; if (canIgnore(ref)) { summ = generateNoOp(ref, isStatic); } else { summ = findSummary(ref); } if (summ != null) { SummarizedMethod n = new SummarizedMethod(ref, summ, m.getDeclaringClass()); syntheticMethods.put(ref, n); return n; } else { syntheticMethods.put(ref, null); return null; } } } /** * Generate a {@link MethodSummary} which is the "standard" representation of a method * that does nothing. */ public static MethodSummary generateStandardNoOp(Language l, MethodReference m, boolean isStatic) { return new NoOpSummary(l, m, isStatic); } /** * Generate a {@link MethodSummary} which is the "standard" representation of a method * that does nothing. Subclasses may override this method to implement alternative semantics * concerning what "do nothing" means. */ public MethodSummary generateNoOp(MethodReference m, boolean isStatic) { Language l = cha.resolveMethod(m).getDeclaringClass().getClassLoader().getLanguage(); return new NoOpSummary(l, m, isStatic); } private static class NoOpSummary extends MethodSummary { private final Language l; public NoOpSummary(Language l, MethodReference method, boolean isStatic) { super(method); setStatic(isStatic); this.l = l; } /* * @see com.ibm.wala.ipa.summaries.MethodSummary#getStatements() */ @Override public SSAInstruction[] getStatements() { if (getReturnType().equals(TypeReference.Void)) { return NO_STATEMENTS; } else { int nullValue = getNumberOfParameters() + 1; SSAInstruction[] result = new SSAInstruction[1]; SSAInstructionFactory insts = l.instructionFactory(); result[0] = insts.ReturnInstruction(0, nullValue, getReturnType().isPrimitiveType()); return result; } } } /** * @param m * @return true iff we can treat m as a no-op method */ protected boolean canIgnore(MemberReference m) { TypeReference T = m.getDeclaringClass(); TypeName n = T.getName(); Atom p = n.getPackage(); return (ignoredPackages.contains(p)); } private MethodSummary findSummary(MemberReference m) { return methodSummaries.get(m); } protected IClassHierarchy getClassHierarchy() { return cha; } }
File
BypassMethodTargetSelector.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
  public MethodSummary(MethodReference method) {
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.summaries;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.MemberReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.warnings.Warning;

/**
 * Summary information for a method.
 */
public class MethodSummary {

  protected final static SSAInstruction[] NO_STATEMENTS = new SSAInstruction[0];

  /**
   * The method summarized
   */
  final private MethodReference method;

  /**
   * List of statements that define this method summary
   */
  private ArrayList statements;

  /**
   * Map: value number -> constant
   */
  private Map constantValues;

  /**
   * The next available program counter value.
   */
  private int nextProgramCounter = 0;

  /**
   * Some reason this method summary indicates a problem.
   */
  private String poison;

  /**
   * An indication of how severe the poison problem is.
   */
  private byte poisonLevel;

  /**
   * Is this a static method?
   */
  private boolean isStatic = false;

  /**
   * Is this a "factory" method?
   */
  private boolean isFactory = false;

    if (method == null) {
      throw new IllegalArgumentException("null method");
    }
    this.method = method;
  }
  
  public int getNumberOfStatements() {
    return (statements == null ? 0 : statements.size());
  }

  public void addStatement(SSAInstruction statement) {
    if (statements == null) {
      statements = new ArrayList();
    }
    statements.add(statement);
  }

  public void addConstant(Integer vn, ConstantValue value) {
    if (constantValues == null)
      constantValues = HashMapFactory.make(5);
    constantValues.put(vn, value);
  }

  /**
   * Returns the method.
   * 
   * @return MethodReference
   */
  public MemberReference getMethod() {
    return method;
  }

  public boolean isNative() {
    // TODO implement this.
    return false;
  }

  /**
   * @param reason
   */
  public void addPoison(String reason) {
    this.poison = reason;
  }

  public boolean hasPoison() {
    return poison != null;
  }

  public String getPoison() {
    return poison;
  }

  public void setPoisonLevel(byte b) {
    poisonLevel = b;
    assert b == Warning.MILD || b == Warning.MODERATE || b == Warning.SEVERE;
  }

  public byte getPoisonLevel() {
    return poisonLevel;
  }

  public SSAInstruction[] getStatements() {
    if (statements == null) {
      return NO_STATEMENTS;
    } else {
      SSAInstruction[] result = new SSAInstruction[statements.size()];
      Iterator it = statements.iterator();
      for (int i = 0; i < result.length; i++) {
        result[i] = it.next();
      }
      return result;
    }
  }

  public Map getConstants() {
    return constantValues;
  }

  /**
   * @return the number of parameters, including the implicit 'this'
   */
  public int getNumberOfParameters() {
    return (isStatic()) ? method.getNumberOfParameters() : method.getNumberOfParameters() + 1;
  }

  public boolean isStatic() {
    return isStatic;
  }

  public void setStatic(boolean b) {
    isStatic = b;
  }

  public TypeReference getReturnType() {
    return method.getReturnType();
  }

  @Override
  public String toString() {
    return "[Summary: " + method + "]";
  }

  /**
   * Note that by convention, getParameterType(0) == this for non-static methods.
   */
  public TypeReference getParameterType(int i) {
    if (isStatic()) {
      return method.getParameterType(i);
    } else {
      if (i == 0) {
        return method.getDeclaringClass();
      } else {
        return method.getParameterType(i - 1);
      }
    }
  }

  public int getNextProgramCounter() {
    return nextProgramCounter++;
  }

  /**
   * Record if this is a "factory" method; meaning it returns some object which we know little about ... usually we'll resolve this
   * based on downstream uses of the object
   * 
   * @param b
   */
  public void setFactory(boolean b) {
    this.isFactory = b;
  }

  public boolean isFactory() {
    return isFactory;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.summaries;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.MemberReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.warnings.Warning;

/**
 * Summary information for a method.
 */
public class MethodSummary {

  protected final static SSAInstruction[] NO_STATEMENTS = new SSAInstruction[0];

  /**
   * The method summarized
   */
  final private MethodReference method;

  /**
   * List of statements that define this method summary
   */
  private ArrayList statements;

  /**
   * Map: value number -> constant
   */
  private Map constantValues;

  /**
   * The next available program counter value.
   */
  private int nextProgramCounter = 0;

  /**
   * Some reason this method summary indicates a problem.
   */
  private String poison;

  /**
   * An indication of how severe the poison problem is.
   */
  private byte poisonLevel;

  /**
   * Is this a static method?
   */
  private boolean isStatic = false;

  /**
   * Is this a "factory" method?
   */
  private boolean isFactory = false;

  public MethodSummary(MethodReference method) {
    if (method == null) {
      throw new IllegalArgumentException("null method");
    }
    this.method = method;
  }

  public void addStatement(SSAInstruction statement) {
    if (statements == null) {
      statements = new ArrayList();
    }
    statements.add(statement);
  }

  public void addConstant(Integer vn, ConstantValue value) {
    if (constantValues == null)
      constantValues = HashMapFactory.make(5);
    constantValues.put(vn, value);
  }

  /**
   * Returns the method.
   * 
   * @return MethodReference
   */
  public MemberReference getMethod() {
    return method;
  }

  public boolean isNative() {
    // TODO implement this.
    return false;
  }

  /**
   * @param reason
   */
  public void addPoison(String reason) {
    this.poison = reason;
  }

  public boolean hasPoison() {
    return poison != null;
  }

  public String getPoison() {
    return poison;
  }

  public void setPoisonLevel(byte b) {
    poisonLevel = b;
    assert b == Warning.MILD || b == Warning.MODERATE || b == Warning.SEVERE;
  }

  public byte getPoisonLevel() {
    return poisonLevel;
  }

  public SSAInstruction[] getStatements() {
    if (statements == null) {
      return NO_STATEMENTS;
    } else {
      SSAInstruction[] result = new SSAInstruction[statements.size()];
      Iterator it = statements.iterator();
      for (int i = 0; i < result.length; i++) {
        result[i] = it.next();
      }
      return result;
    }
  }

  public Map getConstants() {
    return constantValues;
  }

  /**
   * @return the number of parameters, including the implicit 'this'
   */
  public int getNumberOfParameters() {
    return (isStatic()) ? method.getNumberOfParameters() : method.getNumberOfParameters() + 1;
  }

  public boolean isStatic() {
    return isStatic;
  }

  public void setStatic(boolean b) {
    isStatic = b;
  }

  public TypeReference getReturnType() {
    return method.getReturnType();
  }

  @Override
  public String toString() {
    return "[Summary: " + method + "]";
  }

  /**
   * Note that by convention, getParameterType(0) == this for non-static methods.
   */
  public TypeReference getParameterType(int i) {
    if (isStatic()) {
      return method.getParameterType(i);
    } else {
      if (i == 0) {
        return method.getDeclaringClass();
      } else {
        return method.getParameterType(i - 1);
      }
    }
  }

  public int getNextProgramCounter() {
    return nextProgramCounter++;
  }

  /**
   * Record if this is a "factory" method; meaning it returns some object which we know little about ... usually we'll resolve this
   * based on downstream uses of the object
   * 
   * @param b
   */
  public void setFactory(boolean b) {
    this.isFactory = b;
  }

  public boolean isFactory() {
    return isFactory;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
   */
  }
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.summaries;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.MemberReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.warnings.Warning;

/**
 * Summary information for a method.
 */
public class MethodSummary {

  protected final static SSAInstruction[] NO_STATEMENTS = new SSAInstruction[0];

  /**
   * The method summarized
   */
  final private MethodReference method;

  /**
   * List of statements that define this method summary
   */
  private ArrayList statements;

  /**
   * Map: value number -> constant
   */
  private Map constantValues;

  /**
   * The next available program counter value.
   */
  private int nextProgramCounter = 0;

  /**
   * Some reason this method summary indicates a problem.
   */
  private String poison;

  /**
   * An indication of how severe the poison problem is.
   */
  private byte poisonLevel;

  /**
   * Is this a static method?
   */
  private boolean isStatic = false;

  /**
   * Is this a "factory" method?
   */
  private boolean isFactory = false;

  public MethodSummary(MethodReference method) {
    if (method == null) {
      throw new IllegalArgumentException("null method");
    }
    this.method = method;
  }
  
  public int getNumberOfStatements() {
    return (statements == null ? 0 : statements.size());
  }

  public void addStatement(SSAInstruction statement) {
    if (statements == null) {
      statements = new ArrayList();
    }
    statements.add(statement);
  }

  public void addConstant(Integer vn, ConstantValue value) {
    if (constantValues == null)
      constantValues = HashMapFactory.make(5);
    constantValues.put(vn, value);
  }

  /**
   * Returns the method.
   * 
   * @return MethodReference
  public MemberReference getMethod() {
    return method;
  }

  public boolean isNative() {
    // TODO implement this.
    return false;
  }

  /**
   * @param reason
   */
  public void addPoison(String reason) {
    this.poison = reason;
  }

  public boolean hasPoison() {
    return poison != null;

  public String getPoison() {
    return poison;
  }

  public void setPoisonLevel(byte b) {
    poisonLevel = b;
    assert b == Warning.MILD || b == Warning.MODERATE || b == Warning.SEVERE;
  }

  public byte getPoisonLevel() {
    return poisonLevel;
  }

  public SSAInstruction[] getStatements() {
    if (statements == null) {
      return NO_STATEMENTS;
    } else {
      SSAInstruction[] result = new SSAInstruction[statements.size()];
      Iterator it = statements.iterator();
      for (int i = 0; i < result.length; i++) {
        result[i] = it.next();
      }
      return result;
    }
  }

  public Map getConstants() {
    return constantValues;
  }

  /**
   * @return the number of parameters, including the implicit 'this'
   */
  public int getNumberOfParameters() {
    return (isStatic()) ? method.getNumberOfParameters() : method.getNumberOfParameters() + 1;
  }

  public boolean isStatic() {
    return isStatic;
  }

  public void setStatic(boolean b) {
    isStatic = b;
  }

  public TypeReference getReturnType() {
    return method.getReturnType();
  }

  @Override
  public String toString() {
    return "[Summary: " + method + "]";
  }

  /**
   * Note that by convention, getParameterType(0) == this for non-static methods.
   */
  public TypeReference getParameterType(int i) {
    if (isStatic()) {
      return method.getParameterType(i);
    } else {
      if (i == 0) {
        return method.getDeclaringClass();
      } else {
        return method.getParameterType(i - 1);
      }
    }
  }

  public int getNextProgramCounter() {
    return nextProgramCounter++;
  }

  /**
   * Record if this is a "factory" method; meaning it returns some object which we know little about ... usually we'll resolve this
   * based on downstream uses of the object
   * 
   * @param b
   */
  public void setFactory(boolean b) {
    this.isFactory = b;
  }

  public boolean isFactory() {
    return isFactory;
  }

}
File
MethodSummary.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ipa.summaries;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.shrikeBT.BytecodeConstants;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.warnings.Warning;

/**
 * This class reads method summaries from an XML Stream.
 */
public class XMLMethodSummaryReader implements BytecodeConstants {

  static final boolean DEBUG = false;

  /**
   * Governing analysis scope
   */
  final private AnalysisScope scope;

  /**
   * Method summaries collected for methods
   */
  final private HashMap summaries = HashMapFactory.make();

  /**
   * Set of TypeReferences that are marked as "allocatable"
   */
  final private HashSet allocatable = HashSetFactory.make();

  /**
   * Set of Atoms that represent packages that can be ignored
   */
  final private HashSet ignoredPackages = HashSetFactory.make();

  //
  // Define XML element names
  //
  private final static int E_CLASSLOADER = 0;

  private final static int E_METHOD = 1;

  private final static int E_CLASS = 2;

  private final static int E_PACKAGE = 3;

  private final static int E_CALL = 4;

  private final static int E_NEW = 5;

  private final static int E_POISON = 6;

  private final static int E_SUMMARY_SPEC = 7;

  private final static int E_RETURN = 8;

  private final static int E_PUTSTATIC = 9;

        break;
  private final static int E_AASTORE = 10;

  private final static int E_PUTFIELD = 11;

  private final static int E_GETFIELD = 12;

  private final static int E_ATHROW = 13;

  private final static int E_CONSTANT = 14;

  private final static Map elementMap = HashMapFactory.make(14);
  static {
    elementMap.put("classloader", new Integer(E_CLASSLOADER));
    elementMap.put("method", new Integer(E_METHOD));
    elementMap.put("class", new Integer(E_CLASS));
    elementMap.put("package", new Integer(E_PACKAGE));
    elementMap.put("call", new Integer(E_CALL));
    elementMap.put("new", new Integer(E_NEW));
    elementMap.put("poison", new Integer(E_POISON));
    elementMap.put("summary-spec", new Integer(E_SUMMARY_SPEC));
    elementMap.put("return", new Integer(E_RETURN));
    elementMap.put("putstatic", new Integer(E_PUTSTATIC));
    elementMap.put("aastore", new Integer(E_AASTORE));
    elementMap.put("putfield", new Integer(E_PUTFIELD));
    elementMap.put("getfield", new Integer(E_GETFIELD));
    elementMap.put("throw", new Integer(E_ATHROW));
    elementMap.put("constant", new Integer(E_CONSTANT));
  }

  //
  // Define XML attribute names
  //
  private final static String A_NAME = "name";

  private final static String A_TYPE = "type";

  private final static String A_CLASS = "class";

  private final static String A_SIZE = "size";

  private final static String A_DESCRIPTOR = "descriptor";

  private final static String A_REASON = "reason";

  private final static String A_LEVEL = "level";

  private final static String A_WILDCARD = "*";

  private final static String A_DEF = "def";

  private final static String A_STATIC = "static";

  private final static String A_VALUE = "value";

  private final static String A_FIELD = "field";

  private final static String A_FIELD_TYPE = "fieldType";

  private final static String A_ARG = "arg";

  private final static String A_ALLOCATABLE = "allocatable";

  private final static String A_REF = "ref";

  private final static String A_INDEX = "index";

  private final static String A_IGNORE = "ignore";

  private final static String A_FACTORY = "factory";

  private final static String A_NUM_ARGS = "numArgs";

  private final static String V_NULL = "null";

  private final static String V_TRUE = "true";

  public XMLMethodSummaryReader(InputStream xmlFile, AnalysisScope scope) {
    super();
    if (xmlFile == null) {
      throw new IllegalArgumentException("null xmlFile");
    }
    if (scope == null) {
      throw new IllegalArgumentException("null scope");
    }
    this.scope = scope;
    try {
      readXML(xmlFile);
    } catch (Exception e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
    }
  }

  private void readXML(InputStream xml) throws SAXException, IOException, ParserConfigurationException {
    SAXHandler handler = new SAXHandler();

    assert xml != null : "Null xml stream";
    SAXParserFactory factory = SAXParserFactory.newInstance();
    factory.newSAXParser().parse(new InputSource(xml), handler);
  }

  /**
      case E_PUTFIELD:
   * @return Method summaries collected for methods. Mapping Object -> MethodSummary where Object is either a
   *         
    *
  • MethodReference *
  • TypeReference *
  • Atom (package name) *
*/ public Map getSummaries() { return summaries; } /** * @return Set of TypeReferences marked "allocatable" */ public Set getAllocatableClasses() { return allocatable; } /** * @return Set of Atoms representing ignorable packages */ public Set getIgnoredPackages() { return ignoredPackages; } /** * @author sfink * * SAX parser logic for XML method summaries */ private class SAXHandler extends DefaultHandler { /** * The class loader reference for the element being processed */ private ClassLoaderReference governingLoader = null; /** * The method summary for the element being processed */ private MethodSummary governingMethod = null; /** * The declaring class for the element begin processed */ private TypeReference governingClass = null; /** * The package for the element being processed */ private Atom governingPackage = null; /** * The next available local number for the method being processed */ private int nextLocal = -1; /** * A mapping from String (variable name) -> Integer (local number) */ private Map symbolTable = null; /* * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) */ @Override case E_GETFIELD: public void startElement(String uri, String name, String qName, Attributes atts) { Integer element = elementMap.get(qName); if (element == null) { Assertions.UNREACHABLE("Invalid element: " + qName); } switch (element.intValue()) { case E_CLASSLOADER: { String clName = atts.getValue(A_NAME); governingLoader = classLoaderName2Ref(clName); } break; case E_METHOD: String mname = atts.getValue(A_NAME); if (mname.equals(A_WILDCARD)) { Assertions.UNREACHABLE("Wildcards not currently implemented."); } else { startMethod(atts); } break; case E_CLASS: String cname = atts.getValue(A_NAME); if (cname.equals(A_WILDCARD)) { Assertions.UNREACHABLE("Wildcards not currently implemented"); } else { startClass(cname, atts); } break; case E_PACKAGE: governingPackage = Atom.findOrCreateUnicodeAtom(atts.getValue(A_NAME)); String ignore = atts.getValue(A_IGNORE); if (ignore != null && ignore.equals(V_TRUE)) { ignoredPackages.add(governingPackage); } break; case E_CALL: processCallSite(atts); break; case E_NEW: processAllocation(atts); break; case E_PUTSTATIC: processPutStatic(atts); processPutField(atts); break; case E_GETFIELD: processGetField(atts); break; case E_ATHROW: processAthrow(atts); break; case E_AASTORE: processAastore(atts); break; case E_RETURN: processReturn(atts); break; case E_POISON: processPoison(atts); break; case E_CONSTANT: processConstant(atts); break; case E_SUMMARY_SPEC: break; default: Assertions.UNREACHABLE("Unexpected element: " + name); break; } } private void startClass(String cname, Attributes atts) { String clName = "L" + governingPackage + "/" + cname; governingClass = className2Ref(clName); String allocString = atts.getValue(A_ALLOCATABLE); if (allocString != null) { Assertions.productionAssertion(allocString.equals("true")); allocatable.add(governingClass); } } /* * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ @Override public void endElement(String uri, String name, String qName) { Integer element = elementMap.get(qName); if (element == null) { Assertions.UNREACHABLE("Invalid element: " + name); } switch (element.intValue()) { case E_CLASSLOADER: governingLoader = null; break; case E_METHOD: if (governingMethod != null) { checkReturnValue(governingMethod); } governingMethod = null; symbolTable = null; break; case E_CLASS: governingClass = null; break; case E_PACKAGE: governingPackage = null; break; case E_CALL: case E_NEW: case E_POISON: case E_PUTSTATIC: case E_PUTFIELD: case E_AASTORE: case E_ATHROW: case E_SUMMARY_SPEC: case E_RETURN: case E_CONSTANT: break; default: Assertions.UNREACHABLE("Unexpected element: " + name); break; } } /** * If a method is declared to return a value, be sure the method summary includes a return statement. Throw an assertion if not. * * @param governingMethod */ private void checkReturnValue(MethodSummary governingMethod) { Assertions.productionAssertion(governingMethod != null); Assertions.productionAssertion(governingMethod.getReturnType() != null); if (governingMethod.getReturnType().isReferenceType()) { SSAInstruction[] statements = governingMethod.getStatements(); for (int i = 0; i < statements.length; i++) { if (statements[i] instanceof SSAReturnInstruction) { return; } } Assertions.UNREACHABLE("Method summary " + governingMethod + " must have a return statement."); } } /** * Process an element indicating a call instruction * * @param atts */ private void processCallSite(Attributes atts) { */ String typeString = atts.getValue(A_TYPE); String nameString = atts.getValue(A_NAME); String classString = atts.getValue(A_CLASS); String descString = atts.getValue(A_DESCRIPTOR); TypeReference type = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(classString)); Atom nm = Atom.findOrCreateAsciiAtom(nameString); Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); Descriptor D = Descriptor.findOrCreateUTF8(lang, descString); MethodReference ref = MethodReference.findOrCreate(type, nm, D); CallSiteReference site = null; int nParams = ref.getNumberOfParameters(); if (typeString.equals("virtual")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.VIRTUAL); nParams++; } else if (typeString.equals("special")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.SPECIAL); nParams++; } else if (typeString.equals("interface")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.INTERFACE); nParams++; } else if (typeString.equals("static")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.STATIC); } else { Assertions.UNREACHABLE("Invalid call type " + typeString); } int[] params = new int[nParams]; for (int i = 0; i < params.length; i++) { String argString = atts.getValue(A_ARG + i); Assertions.productionAssertion(argString != null, "unspecified arg in method " + governingMethod + " " + site); Integer valueNumber = symbolTable.get(argString); if (valueNumber == null) { Assertions.UNREACHABLE("Cannot lookup value: " + argString); } params[i] = valueNumber.intValue(); } // allocate local for exceptions int exceptionValue = nextLocal++; // register the local variable defined by this call, if appropriate String defVar = atts.getValue(A_DEF); if (defVar != null) { if (symbolTable.keySet().contains(defVar)) { Assertions.UNREACHABLE("Cannot def variable twice: " + defVar + " in " + governingMethod); } int defNum = nextLocal; symbolTable.put(defVar, new Integer(nextLocal++)); governingMethod.addStatement(insts.InvokeInstruction(governingMethod.getNumberOfStatements(), defNum, params, exceptionValue, site)); } else { // ignore return value, if any governingMethod.addStatement(insts.InvokeInstruction(governingMethod.getNumberOfStatements(), params, exceptionValue, site)); } } /** * Process an element indicating a new allocation site. * * @param atts */ private void processAllocation(Attributes atts) { Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); // deduce the concrete type allocated String classString = atts.getValue(A_CLASS); final TypeReference type = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(classString)); // register the local variable defined by this allocation String defVar = atts.getValue(A_DEF); if (symbolTable.keySet().contains(defVar)) { Assertions.UNREACHABLE("Cannot def variable twice: " + defVar + " in " + governingMethod); } if (defVar == null) { // the method summary ignores the def'ed variable. // just allocate a temporary defVar = "L" + nextLocal; } int defNum = nextLocal; symbolTable.put(defVar, new Integer(nextLocal++)); // create the allocation statement and add it to the method summary case E_POISON: NewSiteReference ref = NewSiteReference.make(governingMethod.getNextProgramCounter(), type); SSANewInstruction a = null; if (type.isArrayType()) { String size = atts.getValue(A_SIZE); Assertions.productionAssertion(size != null); Integer sNumber = symbolTable.get(size); Assertions.productionAssertion(sNumber != null); Assertions.productionAssertion(type.getDimensionality() == 1); a = insts.NewInstruction(governingMethod.getNumberOfStatements(), defNum, ref, new int[] { sNumber.intValue() }); } else { a = insts.NewInstruction(governingMethod.getNumberOfStatements(), defNum, ref); } governingMethod.addStatement(a); } /** * Process an element indicating an Athrow * * @param atts */ private void processAthrow(Attributes atts) { Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); // get the value thrown String V = atts.getValue(A_VALUE); if (V == null) { Assertions.UNREACHABLE("Must specify value for putfield " + governingMethod); } Integer valueNumber = symbolTable.get(V); if (valueNumber == null) { Assertions.UNREACHABLE("Cannot lookup value: " + V); } SSAThrowInstruction T = insts.ThrowInstruction(governingMethod.getNumberOfStatements(), valueNumber.intValue()); governingMethod.addStatement(T); } /** * Process an element indicating a putfield. * * @param atts */ private void processGetField(Attributes atts) { Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); // deduce the field written String classString = atts.getValue(A_CLASS); TypeReference type = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(classString)); String fieldString = atts.getValue(A_FIELD); Atom fieldName = Atom.findOrCreateAsciiAtom(fieldString); String ftString = atts.getValue(A_FIELD_TYPE); TypeReference fieldType = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(ftString)); FieldReference field = FieldReference.findOrCreate(type, fieldName, fieldType); // get the value def'fed String defVar = atts.getValue(A_DEF); if (symbolTable.keySet().contains(defVar)) { Assertions.UNREACHABLE("Cannot def variable twice: " + defVar + " in " + governingMethod); } if (defVar == null) { Assertions.UNREACHABLE("Must specify def for getfield " + governingMethod); } int defNum = nextLocal; symbolTable.put(defVar, new Integer(nextLocal++)); // get the ref read from String R = atts.getValue(A_REF); if (R == null) { Assertions.UNREACHABLE("Must specify ref for getfield " + governingMethod); } Integer refNumber = symbolTable.get(R); if (refNumber == null) { Assertions.UNREACHABLE("Cannot lookup ref: " + R); } SSAGetInstruction G = insts.GetInstruction(governingMethod.getNumberOfStatements(), defNum, refNumber.intValue(), field); governingMethod.addStatement(G); } /** * Process an element indicating a putfield. * * @param atts */ private void processPutField(Attributes atts) { Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); // deduce the field written * @param atts String classString = atts.getValue(A_CLASS); TypeReference type = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(classString)); String fieldString = atts.getValue(A_FIELD); Atom fieldName = Atom.findOrCreateAsciiAtom(fieldString); String ftString = atts.getValue(A_FIELD_TYPE); TypeReference fieldType = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(ftString)); FieldReference field = FieldReference.findOrCreate(type, fieldName, fieldType); // get the value stored String V = atts.getValue(A_VALUE); if (V == null) { Assertions.UNREACHABLE("Must specify value for putfield " + governingMethod); } Integer valueNumber = symbolTable.get(V); if (valueNumber == null) { Assertions.UNREACHABLE("Cannot lookup value: " + V); } // get the ref stored to String R = atts.getValue(A_REF); if (R == null) { Assertions.UNREACHABLE("Must specify ref for putfield " + governingMethod); } Integer refNumber = symbolTable.get(R); if (refNumber == null) { Assertions.UNREACHABLE("Cannot lookup ref: " + R); } SSAPutInstruction P = insts.PutInstruction(governingMethod.getNumberOfStatements(), refNumber.intValue(), valueNumber.intValue(), field); governingMethod.addStatement(P); } /** * Process an element indicating a putstatic. * * @param atts */ private void processPutStatic(Attributes atts) { Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); // deduce the field written String classString = atts.getValue(A_CLASS); TypeReference type = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(classString)); String fieldString = atts.getValue(A_FIELD); Atom fieldName = Atom.findOrCreateAsciiAtom(fieldString); String ftString = atts.getValue(A_FIELD_TYPE); TypeReference fieldType = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(ftString)); break; FieldReference field = FieldReference.findOrCreate(type, fieldName, fieldType); // get the value stored String V = atts.getValue(A_VALUE); if (V == null) { Assertions.UNREACHABLE("Must specify value for putstatic " + governingMethod); } Integer valueNumber = symbolTable.get(V); if (valueNumber == null) { Assertions.UNREACHABLE("Cannot lookup value: " + V); } SSAPutInstruction P = insts.PutInstruction(governingMethod.getNumberOfStatements(), valueNumber.intValue(), field); governingMethod.addStatement(P); } /** * Process an element indicating an Aastore * * @param atts */ private void processAastore(Attributes atts) { Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); String R = atts.getValue(A_REF); if (R == null) { Assertions.UNREACHABLE("Must specify ref for aastore " + governingMethod); } Integer refNumber = symbolTable.get(R); if (refNumber == null) { Assertions.UNREACHABLE("Cannot lookup value: " + R); } // N.B: we currently ignore the index String I = atts.getValue(A_INDEX); if (I == null) { Assertions.UNREACHABLE("Must specify index for aastore " + governingMethod); } String V = atts.getValue(A_VALUE); if (V == null) { Assertions.UNREACHABLE("Must specify value for aastore " + governingMethod); } Integer valueNumber = symbolTable.get(V); if (valueNumber == null) { Assertions.UNREACHABLE("Cannot lookup value: " + V); } SSAArrayStoreInstruction S = insts.ArrayStoreInstruction(governingMethod.getNumberOfStatements(), refNumber.intValue(), 0, valueNumber.intValue(), TypeReference.JavaLangObject); governingMethod.addStatement(S); } /** * Process an element indicating a return statement. * * @param atts */ private void processReturn(Attributes atts) { Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); if (governingMethod.getReturnType() != null) { String retV = atts.getValue(A_VALUE); if (retV == null) { SSAReturnInstruction R = insts.ReturnInstruction(governingMethod.getNumberOfStatements()); governingMethod.addStatement(R); } else { Integer valueNumber = symbolTable.get(retV); if (valueNumber == null) { if (!retV.equals(V_NULL)) { Assertions.UNREACHABLE("Cannot return value with no def: " + retV); } else { valueNumber = symbolTable.get(V_NULL); if (valueNumber == null) { valueNumber = new Integer(nextLocal++); symbolTable.put(V_NULL, valueNumber); } } } boolean isPrimitive = governingMethod.getReturnType().isPrimitiveType(); SSAReturnInstruction R = insts.ReturnInstruction(governingMethod.getNumberOfStatements(), valueNumber.intValue(), isPrimitive); governingMethod.addStatement(R); } } } /** * @param atts */ private void processConstant(Attributes atts) { String var = atts.getValue(A_NAME); if (var == null) Assertions.UNREACHABLE("Must give name for constant"); Integer valueNumber = new Integer(nextLocal++); symbolTable.put(var, valueNumber); String typeString = atts.getValue(A_TYPE); } String valueString = atts.getValue(A_VALUE); governingMethod.addConstant(valueNumber, (typeString.equals("int")) ? new ConstantValue(new Integer(valueString)) : (typeString.equals("long")) ? new ConstantValue(new Long(valueString)) : (typeString.equals("short")) ? new ConstantValue(new Short(valueString)) : (typeString.equals("float")) ? new ConstantValue(new Float(valueString)) : (typeString.equals("double")) ? new ConstantValue(new Double(valueString)) : null); } /** * Process an element which indicates this method is "poison" * * @param atts */ private void processPoison(Attributes atts) { String reason = atts.getValue(A_REASON); governingMethod.addPoison(reason); String level = atts.getValue(A_LEVEL); if (level.equals("severe")) { governingMethod.setPoisonLevel(Warning.SEVERE); } else if (level.equals("moderate")) { governingMethod.setPoisonLevel(Warning.MODERATE); } else if (level.equals("mild")) { governingMethod.setPoisonLevel(Warning.MILD); } else { Assertions.UNREACHABLE("Unexpected level: " + level); } } /** * Begin processing of a method. 1. Set the governing method. 2. Initialize the nextLocal variable * * @param atts */ private void startMethod(Attributes atts) { String methodName = atts.getValue(A_NAME); Atom mName = Atom.findOrCreateUnicodeAtom(methodName); String descString = atts.getValue(A_DESCRIPTOR); Language lang = scope.getLanguage(governingLoader.getLanguage()); Descriptor D = Descriptor.findOrCreateUTF8(lang, descString); MethodReference ref = MethodReference.findOrCreate(governingClass, mName, D); governingMethod = new MethodSummary(ref); if (DEBUG) { System.err.println(("Register method summary: " + ref)); } summaries.put(ref, governingMethod); boolean isStatic = false; String staticString = atts.getValue(A_STATIC); if (staticString != null) { if (staticString.equals("true")) { isStatic = true; governingMethod.setStatic(true); } else if (staticString.equals("false")) { isStatic = false; governingMethod.setStatic(false); } else { Assertions.UNREACHABLE("Invalid attribute value " + A_STATIC + ": " + staticString); } } String factoryString = atts.getValue(A_FACTORY); if (factoryString != null) { if (factoryString.equals("true")) { governingMethod.setFactory(true); } else if (factoryString.equals("false")) { governingMethod.setFactory(false); } else { Assertions.UNREACHABLE("Invalid attribute value " + A_FACTORY + ": " + factoryString); } } // This is somewhat gross, but it is to deal with the fact that // some non-Java languages have notions of arguments that do not // map nicely to descriptors. int nParams; String specifiedArgs = atts.getValue(A_NUM_ARGS); if (specifiedArgs == null) { nParams = ref.getNumberOfParameters(); if (!isStatic) { nParams += 1; } } else { nParams = Integer.parseInt(specifiedArgs); } // note that symbol tables reserve v0 for "unknown", so v1 gets assigned // to the first parameter "arg0", and so forth. nextLocal = nParams + 1; symbolTable = HashMapFactory.make(5); // create symbols for the parameters for (int i = 0; i < nParams; i++) { symbolTable.put("arg" + i, new Integer(i + 1)); } } /** * Method classLoaderName2Ref. * * @param clName * @return ClassLoaderReference */ private ClassLoaderReference classLoaderName2Ref(String clName) { return scope.getLoader(Atom.findOrCreateUnicodeAtom(clName)); } /** * Method classLoaderName2Ref. * * @param clName * @return ClassLoaderReference */ private TypeReference className2Ref(String clName) { return TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(clName)); } } } ======= /******************************************************************************* * Copyright (c) 2002 - 2006 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.ipa.summaries; import static com.ibm.wala.types.TypeName.ArrayMask; import static com.ibm.wala.types.TypeName.ElementBits; import static com.ibm.wala.types.TypeName.PrimitiveMask; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.classLoader.Language; import com.ibm.wala.classLoader.NewSiteReference; import com.ibm.wala.ipa.callgraph.AnalysisScope; import com.ibm.wala.shrikeBT.BytecodeConstants; import com.ibm.wala.shrikeBT.IInvokeInstruction; import com.ibm.wala.ssa.ConstantValue; import com.ibm.wala.ssa.SSAArrayStoreInstruction; import com.ibm.wala.ssa.SSAGetInstruction; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSAInstructionFactory; import com.ibm.wala.ssa.SSANewInstruction; import com.ibm.wala.ssa.SSAPutInstruction; import com.ibm.wala.ssa.SSAReturnInstruction; import com.ibm.wala.ssa.SSAThrowInstruction; import com.ibm.wala.types.ClassLoaderReference; import com.ibm.wala.types.Descriptor; import com.ibm.wala.types.FieldReference; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeName; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.collections.HashMapFactory; import com.ibm.wala.util.collections.HashSetFactory; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.strings.Atom; import com.ibm.wala.util.warnings.Warning; /** * This class reads method summaries from an XML Stream. */ public class XMLMethodSummaryReader implements BytecodeConstants { static final boolean DEBUG = false; /** * Governing analysis scope */ final private AnalysisScope scope; /** * Method summaries collected for methods */ } final private HashMap summaries = HashMapFactory.make(); /** * Set of TypeReferences that are marked as "allocatable" */ final private HashSet allocatable = HashSetFactory.make(); /** * Set of Atoms that represent packages that can be ignored */ final private HashSet ignoredPackages = HashSetFactory.make(); // // Define XML element names // private final static int E_CLASSLOADER = 0; private final static int E_METHOD = 1; private final static int E_CLASS = 2; private final static int E_PACKAGE = 3; private final static int E_CALL = 4; private final static int E_NEW = 5; private final static int E_POISON = 6; private final static int E_SUMMARY_SPEC = 7; private final static int E_RETURN = 8; private final static int E_PUTSTATIC = 9; private final static int E_AASTORE = 10; private final static int E_PUTFIELD = 11; private final static int E_GETFIELD = 12; private final static int E_ATHROW = 13; private final static int E_CONSTANT = 14; private final static Map elementMap = HashMapFactory.make(14); static { */ elementMap.put("classloader", new Integer(E_CLASSLOADER)); elementMap.put("method", new Integer(E_METHOD)); elementMap.put("class", new Integer(E_CLASS)); elementMap.put("package", new Integer(E_PACKAGE)); elementMap.put("call", new Integer(E_CALL)); elementMap.put("new", new Integer(E_NEW)); elementMap.put("poison", new Integer(E_POISON)); elementMap.put("summary-spec", new Integer(E_SUMMARY_SPEC)); elementMap.put("return", new Integer(E_RETURN)); elementMap.put("putstatic", new Integer(E_PUTSTATIC)); elementMap.put("aastore", new Integer(E_AASTORE)); elementMap.put("putfield", new Integer(E_PUTFIELD)); elementMap.put("getfield", new Integer(E_GETFIELD)); elementMap.put("throw", new Integer(E_ATHROW)); elementMap.put("constant", new Integer(E_CONSTANT)); } // // Define XML attribute names // private final static String A_NAME = "name"; private final static String A_TYPE = "type"; private final static String A_CLASS = "class"; private final static String A_SIZE = "size"; private final static String A_DESCRIPTOR = "descriptor"; private final static String A_REASON = "reason"; private final static String A_LEVEL = "level"; private final static String A_WILDCARD = "*"; private final static String A_DEF = "def"; private final static String A_STATIC = "static"; private final static String A_VALUE = "value"; private final static String A_FIELD = "field"; private final static String A_FIELD_TYPE = "fieldType"; private final static String A_ARG = "arg"; private final static String A_ALLOCATABLE = "allocatable"; private final static String A_REF = "ref"; private final static String A_INDEX = "index"; private final static String A_IGNORE = "ignore"; private final static String A_FACTORY = "factory"; private final static String A_NUM_ARGS = "numArgs"; private final static String V_NULL = "null"; private final static String V_TRUE = "true"; public XMLMethodSummaryReader(InputStream xmlFile, AnalysisScope scope) { super(); if (xmlFile == null) { throw new IllegalArgumentException("null xmlFile"); } if (scope == null) { throw new IllegalArgumentException("null scope"); } this.scope = scope; try { readXML(xmlFile); } catch (Exception e) { e.printStackTrace(); Assertions.UNREACHABLE(); } } private void readXML(InputStream xml) throws SAXException, IOException, ParserConfigurationException { SAXHandler handler = new SAXHandler(); assert xml != null : "Null xml stream"; SAXParserFactory factory = SAXParserFactory.newInstance(); factory.newSAXParser().parse(new InputSource(xml), handler); } /** * @return Method summaries collected for methods. Mapping Object -> MethodSummary where Object is either a *
    *
  • MethodReference *
  • TypeReference *
  • Atom (package name) *
*/ public Map getSummaries() { return summaries; } /** * @return Set of TypeReferences marked "allocatable" public Set getAllocatableClasses() { return allocatable; } /** * @return Set of Atoms representing ignorable packages */ public Set getIgnoredPackages() { return ignoredPackages; } /** * @author sfink * * SAX parser logic for XML method summaries */ private class SAXHandler extends DefaultHandler { /** * The class loader reference for the element being processed */ private ClassLoaderReference governingLoader = null; /** * The method summary for the element being processed */ private MethodSummary governingMethod = null; /** * The declaring class for the element begin processed */ private TypeReference governingClass = null; /** * The package for the element being processed */ private Atom governingPackage = null; /** * The next available local number for the method being processed */ private int nextLocal = -1; /** * A mapping from String (variable name) -> Integer (local number) */ private Map symbolTable = null; /* * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) */ @Override public void startElement(String uri, String name, String qName, Attributes atts) { Integer element = elementMap.get(qName); if (element == null) { Assertions.UNREACHABLE("Invalid element: " + qName); } switch (element.intValue()) { case E_CLASSLOADER: { String clName = atts.getValue(A_NAME); governingLoader = classLoaderName2Ref(clName); } break; case E_METHOD: String mname = atts.getValue(A_NAME); if (mname.equals(A_WILDCARD)) { Assertions.UNREACHABLE("Wildcards not currently implemented."); } else { startMethod(atts); } break; case E_CLASS: String cname = atts.getValue(A_NAME); if (cname.equals(A_WILDCARD)) { Assertions.UNREACHABLE("Wildcards not currently implemented"); } else { startClass(cname, atts); } break; case E_PACKAGE: governingPackage = Atom.findOrCreateUnicodeAtom(atts.getValue(A_NAME)); String ignore = atts.getValue(A_IGNORE); if (ignore != null && ignore.equals(V_TRUE)) { ignoredPackages.add(governingPackage); } break; case E_CALL: processCallSite(atts); break; case E_NEW: processAllocation(atts); break; case E_PUTSTATIC: processPutStatic(atts); break; case E_PUTFIELD: processPutField(atts); break; case E_GETFIELD: processGetField(atts); break; case E_ATHROW: processAthrow(atts); break; case E_AASTORE: processAastore(atts); break; case E_RETURN: processReturn(atts); break; processPoison(atts); break; case E_CONSTANT: processConstant(atts); break; case E_SUMMARY_SPEC: break; default: Assertions.UNREACHABLE("Unexpected element: " + name); break; } } private void startClass(String cname, Attributes atts) { String clName = "L" + governingPackage + "/" + cname; governingClass = className2Ref(clName); String allocString = atts.getValue(A_ALLOCATABLE); if (allocString != null) { Assertions.productionAssertion(allocString.equals("true")); allocatable.add(governingClass); } } /* * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ @Override public void endElement(String uri, String name, String qName) { Integer element = elementMap.get(qName); if (element == null) { Assertions.UNREACHABLE("Invalid element: " + name); } switch (element.intValue()) { case E_CLASSLOADER: governingLoader = null; break; case E_METHOD: if (governingMethod != null) { checkReturnValue(governingMethod); } governingMethod = null; symbolTable = null; break; case E_CLASS: governingClass = null; break; case E_PACKAGE: governingPackage = null; break; case E_CALL: case E_GETFIELD: case E_NEW: case E_POISON: case E_PUTSTATIC: case E_PUTFIELD: case E_AASTORE: case E_ATHROW: case E_SUMMARY_SPEC: case E_RETURN: case E_CONSTANT: break; default: Assertions.UNREACHABLE("Unexpected element: " + name); /** * If a method is declared to return a value, be sure the method summary includes a return statement. Throw an assertion if not. * * @param governingMethod */ private void checkReturnValue(MethodSummary governingMethod) { Assertions.productionAssertion(governingMethod != null); Assertions.productionAssertion(governingMethod.getReturnType() != null); if (governingMethod.getReturnType().isReferenceType()) { SSAInstruction[] statements = governingMethod.getStatements(); for (int i = 0; i < statements.length; i++) { if (statements[i] instanceof SSAReturnInstruction) { return; } } Assertions.UNREACHABLE("Method summary " + governingMethod + " must have a return statement."); } } /** * Process an element indicating a call instruction * * @param atts */ private void processCallSite(Attributes atts) { String typeString = atts.getValue(A_TYPE); String nameString = atts.getValue(A_NAME); String classString = atts.getValue(A_CLASS); String descString = atts.getValue(A_DESCRIPTOR); TypeReference type = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(classString)); Atom nm = Atom.findOrCreateAsciiAtom(nameString); Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); Descriptor D = Descriptor.findOrCreateUTF8(lang, descString); private void processReturn(Attributes atts) { MethodReference ref = MethodReference.findOrCreate(type, nm, D); CallSiteReference site = null; int nParams = ref.getNumberOfParameters(); if (typeString.equals("virtual")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.VIRTUAL); nParams++; } else if (typeString.equals("special")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.SPECIAL); nParams++; } else if (typeString.equals("interface")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.INTERFACE); nParams++; } else if (typeString.equals("static")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.STATIC); } else { Assertions.UNREACHABLE("Invalid call type " + typeString); } int[] params = new int[nParams]; for (int i = 0; i < params.length; i++) { String argString = atts.getValue(A_ARG + i); Assertions.productionAssertion(argString != null, "unspecified arg in method " + governingMethod + " " + site); Integer valueNumber = symbolTable.get(argString); if (valueNumber == null) { Assertions.UNREACHABLE("Cannot lookup value: " + argString); } params[i] = valueNumber.intValue(); } // allocate local for exceptions int exceptionValue = nextLocal++; // register the local variable defined by this call, if appropriate String defVar = atts.getValue(A_DEF); if (defVar != null) { if (symbolTable.keySet().contains(defVar)) { Assertions.UNREACHABLE("Cannot def variable twice: " + defVar + " in " + governingMethod); } int defNum = nextLocal; symbolTable.put(defVar, new Integer(nextLocal++)); governingMethod.addStatement(insts.InvokeInstruction(defNum, params, exceptionValue, site)); } else { // ignore return value, if any governingMethod.addStatement(insts.InvokeInstruction(params, exceptionValue, site)); } } /** * Process an element indicating a new allocation site. * * @param atts */ private void processAllocation(Attributes atts) { Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); // deduce the concrete type allocated String classString = atts.getValue(A_CLASS); final TypeReference type = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(classString)); // register the local variable defined by this allocation String defVar = atts.getValue(A_DEF); if (symbolTable.keySet().contains(defVar)) { Assertions.UNREACHABLE("Cannot def variable twice: " + defVar + " in " + governingMethod); } if (defVar == null) { // the method summary ignores the def'ed variable. // just allocate a temporary defVar = "L" + nextLocal; } int defNum = nextLocal; symbolTable.put(defVar, new Integer(nextLocal++)); // create the allocation statement and add it to the method summary NewSiteReference ref = NewSiteReference.make(governingMethod.getNextProgramCounter(), type); SSANewInstruction a = null; if (type.isArrayType()) { String size = atts.getValue(A_SIZE); Assertions.productionAssertion(size != null); Integer sNumber = symbolTable.get(size); Assertions.productionAssertion(sNumber != null); Assertions.productionAssertion( // array of objects type.getDerivedMask()==ArrayMask || // array of primitives type.getDerivedMask()==((ArrayMask<>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
 * Contributors:
 *
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.ibm.wala.ipa.summaries;

import static com.ibm.wala.types.TypeName.ArrayMask;
import static com.ibm.wala.types.TypeName.ElementBits;
import static com.ibm.wala.types.TypeName.PrimitiveMask;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.shrikeBT.BytecodeConstants;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.strings.Atom;
import com.ibm.wala.util.warnings.Warning;

/**
 * This class reads method summaries from an XML Stream.
 */
public class XMLMethodSummaryReader implements BytecodeConstants {

  static final boolean DEBUG = false;

  /**
   * Governing analysis scope
   */
  final private AnalysisScope scope;

  /**
   * Method summaries collected for methods
   */
  final private HashMap summaries = HashMapFactory.make();

  /**
   * Set of TypeReferences that are marked as "allocatable"
   */
  final private HashSet allocatable = HashSetFactory.make();

  /**
   * Set of Atoms that represent packages that can be ignored
   */
  final private HashSet ignoredPackages = HashSetFactory.make();

  //
  // Define XML element names
  //
  private final static int E_CLASSLOADER = 0;

  private final static int E_METHOD = 1;

  private final static int E_CLASS = 2;

  private final static int E_PACKAGE = 3;

  private final static int E_CALL = 4;

  private final static int E_NEW = 5;

  private final static int E_POISON = 6;

  private final static int E_SUMMARY_SPEC = 7;

  private final static int E_RETURN = 8;

  private final static int E_PUTSTATIC = 9;

  private final static int E_AASTORE = 10;

  private final static int E_PUTFIELD = 11;

  private final static int E_GETFIELD = 12;

  private final static int E_ATHROW = 13;

  private final static int E_CONSTANT = 14;

  private final static Map elementMap = HashMapFactory.make(14);
  static {
    elementMap.put("classloader", new Integer(E_CLASSLOADER));
    elementMap.put("method", new Integer(E_METHOD));
    elementMap.put("class", new Integer(E_CLASS));
    elementMap.put("package", new Integer(E_PACKAGE));
    elementMap.put("call", new Integer(E_CALL));
   */
    elementMap.put("new", new Integer(E_NEW));
    elementMap.put("poison", new Integer(E_POISON));
    elementMap.put("summary-spec", new Integer(E_SUMMARY_SPEC));
    elementMap.put("return", new Integer(E_RETURN));
    elementMap.put("putstatic", new Integer(E_PUTSTATIC));
    elementMap.put("aastore", new Integer(E_AASTORE));
    elementMap.put("putfield", new Integer(E_PUTFIELD));
    elementMap.put("getfield", new Integer(E_GETFIELD));
    elementMap.put("throw", new Integer(E_ATHROW));
    elementMap.put("constant", new Integer(E_CONSTANT));
  }

  //
  // Define XML attribute names
  //
  private final static String A_NAME = "name";

  private final static String A_TYPE = "type";

  private final static String A_CLASS = "class";

  private final static String A_SIZE = "size";

  private final static String A_DESCRIPTOR = "descriptor";

  private final static String A_REASON = "reason";

  private final static String A_LEVEL = "level";

  private final static String A_WILDCARD = "*";

  private final static String A_DEF = "def";

  private final static String A_STATIC = "static";

  private final static String A_VALUE = "value";

  private final static String A_FIELD = "field";

  private final static String A_FIELD_TYPE = "fieldType";

  private final static String A_ARG = "arg";

  private final static String A_ALLOCATABLE = "allocatable";

  private final static String A_REF = "ref";

  private final static String A_INDEX = "index";

  private final static String A_IGNORE = "ignore";

  private final static String A_FACTORY = "factory";

  private final static String A_NUM_ARGS = "numArgs";

  private final static String V_NULL = "null";

  private final static String V_TRUE = "true";

  public XMLMethodSummaryReader(InputStream xmlFile, AnalysisScope scope) {
    super();
    if (xmlFile == null) {
      throw new IllegalArgumentException("null xmlFile");
    }
    if (scope == null) {
      throw new IllegalArgumentException("null scope");
    }
    this.scope = scope;
    try {
      readXML(xmlFile);
    } catch (Exception e) {
      e.printStackTrace();
      Assertions.UNREACHABLE();
    }
  }

  private void readXML(InputStream xml) throws SAXException, IOException, ParserConfigurationException {
    SAXHandler handler = new SAXHandler();

    assert xml != null : "Null xml stream";
    SAXParserFactory factory = SAXParserFactory.newInstance();
    factory.newSAXParser().parse(new InputSource(xml), handler);
  }

  /**
   * @return Method summaries collected for methods. Mapping Object -> MethodSummary where Object is either a
   *         
    *
  • MethodReference *
  • TypeReference *
  • Atom (package name) *
*/ public Map getSummaries() { return summaries; } /** * @return Set of TypeReferences marked "allocatable" */ public Set getAllocatableClasses() { return allocatable; } /** * @return Set of Atoms representing ignorable packages public Set getIgnoredPackages() { return ignoredPackages; } /** * @author sfink * * SAX parser logic for XML method summaries */ private class SAXHandler extends DefaultHandler { /** * The class loader reference for the element being processed */ private ClassLoaderReference governingLoader = null; /** * The method summary for the element being processed */ private MethodSummary governingMethod = null; /** * The declaring class for the element begin processed */ private TypeReference governingClass = null; /** * The package for the element being processed */ private Atom governingPackage = null; /** * The next available local number for the method being processed */ private int nextLocal = -1; /** * A mapping from String (variable name) -> Integer (local number) */ private Map symbolTable = null; /* * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) */ @Override public void startElement(String uri, String name, String qName, Attributes atts) { Integer element = elementMap.get(qName); if (element == null) { Assertions.UNREACHABLE("Invalid element: " + qName); } switch (element.intValue()) { case E_CLASSLOADER: { String clName = atts.getValue(A_NAME); governingLoader = classLoaderName2Ref(clName); } break; case E_METHOD: String mname = atts.getValue(A_NAME); if (mname.equals(A_WILDCARD)) { Assertions.UNREACHABLE("Wildcards not currently implemented."); } else { startMethod(atts); } break; case E_CLASS: String cname = atts.getValue(A_NAME); if (cname.equals(A_WILDCARD)) { Assertions.UNREACHABLE("Wildcards not currently implemented"); } else { startClass(cname, atts); } break; case E_PACKAGE: governingPackage = Atom.findOrCreateUnicodeAtom(atts.getValue(A_NAME)); String ignore = atts.getValue(A_IGNORE); if (ignore != null && ignore.equals(V_TRUE)) { ignoredPackages.add(governingPackage); } break; case E_CALL: processCallSite(atts); break; case E_NEW: processAllocation(atts); break; case E_PUTSTATIC: processPutStatic(atts); break; case E_PUTFIELD: processPutField(atts); break; case E_GETFIELD: processGetField(atts); break; case E_ATHROW: processAthrow(atts); break; case E_AASTORE: processAastore(atts); break; case E_RETURN: processReturn(atts); break; case E_POISON: processPoison(atts); break; case E_CONSTANT: processConstant(atts); break; case E_SUMMARY_SPEC: break; default: Assertions.UNREACHABLE("Unexpected element: " + name); break; } } private void startClass(String cname, Attributes atts) { String clName = "L" + governingPackage + "/" + cname; governingClass = className2Ref(clName); String allocString = atts.getValue(A_ALLOCATABLE); if (allocString != null) { Assertions.productionAssertion(allocString.equals("true")); allocatable.add(governingClass); } } /* * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ @Override public void endElement(String uri, String name, String qName) { Integer element = elementMap.get(qName); if (element == null) { Assertions.UNREACHABLE("Invalid element: " + name); } switch (element.intValue()) { case E_CLASSLOADER: governingLoader = null; break; case E_METHOD: if (governingMethod != null) { checkReturnValue(governingMethod); } governingMethod = null; symbolTable = null; break; case E_CLASS: governingClass = null; break; case E_PACKAGE: governingPackage = null; break; case E_CALL: case E_GETFIELD: case E_NEW: case E_POISON: case E_PUTSTATIC: case E_PUTFIELD: case E_AASTORE: case E_ATHROW: case E_SUMMARY_SPEC: case E_RETURN: case E_CONSTANT: break; default: Assertions.UNREACHABLE("Unexpected element: " + name); break; } } /** * If a method is declared to return a value, be sure the method summary includes a return statement. Throw an assertion if not. * * @param governingMethod */ private void checkReturnValue(MethodSummary governingMethod) { Assertions.productionAssertion(governingMethod != null); Assertions.productionAssertion(governingMethod.getReturnType() != null); if (governingMethod.getReturnType().isReferenceType()) { SSAInstruction[] statements = governingMethod.getStatements(); for (int i = 0; i < statements.length; i++) { if (statements[i] instanceof SSAReturnInstruction) { return; } } Assertions.UNREACHABLE("Method summary " + governingMethod + " must have a return statement."); } } /** * Process an element indicating a call instruction * * @param atts */ private void processCallSite(Attributes atts) { String typeString = atts.getValue(A_TYPE); String nameString = atts.getValue(A_NAME); String classString = atts.getValue(A_CLASS); String descString = atts.getValue(A_DESCRIPTOR); TypeReference type = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(classString)); Atom nm = Atom.findOrCreateAsciiAtom(nameString); Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); Descriptor D = Descriptor.findOrCreateUTF8(lang, descString); MethodReference ref = MethodReference.findOrCreate(type, nm, D); CallSiteReference site = null; int nParams = ref.getNumberOfParameters(); if (typeString.equals("virtual")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.VIRTUAL); nParams++; } else if (typeString.equals("special")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.SPECIAL); nParams++; } else if (typeString.equals("interface")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.INTERFACE); nParams++; } else if (typeString.equals("static")) { site = CallSiteReference.make(governingMethod.getNextProgramCounter(), ref, IInvokeInstruction.Dispatch.STATIC); } else { Assertions.UNREACHABLE("Invalid call type " + typeString); } int[] params = new int[nParams]; for (int i = 0; i < params.length; i++) { String argString = atts.getValue(A_ARG + i); Assertions.productionAssertion(argString != null, "unspecified arg in method " + governingMethod + " " + site); Integer valueNumber = symbolTable.get(argString); if (valueNumber == null) { Assertions.UNREACHABLE("Cannot lookup value: " + argString); } params[i] = valueNumber.intValue(); } // allocate local for exceptions int exceptionValue = nextLocal++; // register the local variable defined by this call, if appropriate String defVar = atts.getValue(A_DEF); if (defVar != null) { if (symbolTable.keySet().contains(defVar)) { Assertions.UNREACHABLE("Cannot def variable twice: " + defVar + " in " + governingMethod); } int defNum = nextLocal; symbolTable.put(defVar, new Integer(nextLocal++)); governingMethod.addStatement(insts.InvokeInstruction(governingMethod.getNumberOfStatements(), defNum, params, exceptionValue, site)); } else { // ignore return value, if any governingMethod.addStatement(insts.InvokeInstruction(governingMethod.getNumberOfStatements(), params, exceptionValue, site)); } } /** * Process an element indicating a new allocation site. * * @param atts */ private void processAllocation(Attributes atts) { Language lang = scope.getLanguage(governingLoader.getLanguage()); SSAInstructionFactory insts = lang.instructionFactory(); // deduce the concrete type allocated String classString = atts.getValue(A_CLASS); final TypeReference type = TypeReference.findOrCreate(governingLoader, TypeName.string2TypeName(classString)); // register the local variable defined by this allocation String defVar = atts.getValue(A_DEF); if (symbolTable.keySet().contains(defVar)) { Assertions.UNREACHABLE("Cannot def variable twice: " + defVar + " in " + governingMethod); } if (defVar == null) { // the method summary ignores the def'ed variable. // just allocate a temporary defVar = "L" + nextLocal; } int defNum = nextLocal; symbolTable.put(defVar, new Integer(nextLocal++)); // create the allocation statement and add it to the method summary NewSiteReference ref = NewSiteReference.make(governingMethod.getNextProgramCounter(), type); SSANewInstruction a = null; if (type.isArrayType()) { String size = atts.getValue(A_SIZE); Assertions.productionAssertion(size != null); Integer sNumber = symbolTable.get(size); Assertions.productionAssertion(sNumber != null); Assertions.productionAssertion( // array of objects type.getDerivedMask()==ArrayMask || // array of primitives type.getDerivedMask()==((ArrayMask<
File
XMLMethodSummaryReader.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
    }

    try {
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.properties;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Properties;

import com.ibm.wala.util.WalaException;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.io.FileProvider;
import com.ibm.wala.util.io.FileUtil;

public final class WalaProperties {

  public static final String WALA_REPORT = "WALA_report"; //$NON-NLS-1$

  public static final String INPUT_DIR = "input_dir"; //$NON-NLS-1$

  public static final String OUTPUT_DIR = "output_dir"; //$NON-NLS-1$

      defprop.setProperty(J2EE_DIR, "./j2ee");
  public final static String J2SE_DIR = "java_runtime_dir"; //$NON-NLS-1$

  public final static String J2EE_DIR = "j2ee_runtime_dir"; //$NON-NLS-1$

  public final static String ECLIPSE_PLUGINS_DIR = "eclipse_plugins_dir"; //$NON-NLS-1$

  /**
   * Determine the classpath noted in wala.properties for J2SE standard libraries
   * @throws IllegalStateException if there's a problem loading the wala properties
   */
  public static String[] getJ2SEJarFiles() {
    Properties p = null;
    try {
      p = WalaProperties.loadProperties();
    } catch (WalaException e) {
      e.printStackTrace();
      throw new IllegalStateException("problem loading wala.properties");
    }

    String dir = p.getProperty(WalaProperties.J2SE_DIR);
    Assertions.productionAssertion(dir != null);
    return getJarsInDirectory(dir);
  }

  /**
   * @return names of the Jar files holding J2EE libraries
   * @throws IllegalStateException if the J2EE_DIR property is not set
   */
  public static String[] getJ2EEJarFiles() {
    Properties p = null;
    try {
      p = WalaProperties.loadProperties();
    } catch (WalaException e) {
      e.printStackTrace();
      throw new IllegalStateException("problem loading wala.properties");
    }
    String dir = p.getProperty(WalaProperties.J2EE_DIR);
    if (dir == null) {
      throw new IllegalStateException("No J2EE directory specified");
    }
    return getJarsInDirectory(dir);
  }

  public static String[] getJarsInDirectory(String dir) {
    File f = new File(dir);
    Assertions.productionAssertion(f.isDirectory(), "not a directory: " + dir);
    Collection col = FileUtil.listFiles(dir, ".*\\.jar$", true);
    String[] result = new String[col.size()];
    int i = 0;
    for (File jarFile : col) {
      result[i++] = jarFile.getAbsolutePath();
    }
    return result;
  }

  final static String PROPERTY_FILENAME = "wala.properties"; //$NON-NLS-1$

  public static Properties loadProperties() throws WalaException {
      Properties result = loadPropertiesFromFile(WalaProperties.class.getClassLoader(), PROPERTY_FILENAME);

      String outputDir = result.getProperty(OUTPUT_DIR, DefaultPropertiesValues.DEFAULT_OUTPUT_DIR);
      result.setProperty(OUTPUT_DIR, convertToAbsolute(outputDir));

      String walaReport = result.getProperty(WALA_REPORT, DefaultPropertiesValues.DEFAULT_WALA_REPORT_FILENAME);
      result.setProperty(WALA_REPORT, convertToAbsolute(walaReport));

      return result;
    } catch (Exception e) {
      e.printStackTrace();
      throw new WalaException("Unable to set up wala properties ", e);
    }
  }

  static String convertToAbsolute(String path) {
    final File file = new File(path);
    return (file.isAbsolute()) ? file.getAbsolutePath() : WalaProperties.getWalaHomeDir().concat(File.separator).concat(path);
  }

  public static Properties loadPropertiesFromFile(ClassLoader loader, String fileName) throws IOException {
    if (loader == null) {
      throw new IllegalArgumentException("loader is null");
    }
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    final InputStream propertyStream = loader.getResourceAsStream(fileName);
    if (propertyStream == null) {
/** BEGIN Custom change: create default properties if no file exists */
      // create default properties
      Properties defprop = new Properties();
      defprop.setProperty(OUTPUT_DIR, "./out");
      defprop.setProperty(INPUT_DIR, "./in");
      defprop.setProperty(ECLIPSE_PLUGINS_DIR, "./plugins");
      defprop.setProperty(WALA_REPORT, "./wala_report.txt");
      final String j2selib = guessJavaLib();
      defprop.setProperty(J2SE_DIR, j2selib);
      
      return defprop;
/** END Custom change: create default properties if no file exists */
    }
    Properties result = new Properties();
    result.load(propertyStream);
    
    if (!result.containsKey(J2SE_DIR)) {
      final String j2selib = guessJavaLib();
      result.setProperty(J2SE_DIR, j2selib);
    }
    return result;
  }

/** BEGIN Custom change: create default properties if no file exists */
  public static String guessJavaLib() {
    final Properties p = System.getProperties();
    final String os = p.getProperty("os.name");
        
    if (os.contains("Mac OS X")) {
      return "/System/Library/Frameworks/JavaVM.framework/Classes";
    } else {
      final String home = System.getProperty("java.home");
      return home + File.separator + "lib";
    }
  }
  
/** END Custom change: create default properties if no file exists */
  /**
   * @deprecated because when running under eclipse, there may be no such directory.
   * Need to handle that case.
   */
  @Deprecated
  public static String getWalaHomeDir() {
    final String envProperty = System.getProperty("WALA_HOME"); //$NON-NLS-1$
    if (envProperty != null)
      return envProperty;

    final URL url = WalaProperties.class.getClassLoader().getResource("wala.properties"); //$NON-NLS-1$
    if (url == null) {
      return System.getProperty("user.dir"); //$NON-NLS-1$
    } else {
      return new File((new FileProvider()).filePathFromURL(url)).getParentFile().getParentFile().getPath();
    }
  }
  
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.properties;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Properties;

import com.ibm.wala.util.WalaException;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.io.FileProvider;
import com.ibm.wala.util.io.FileUtil;

public final class WalaProperties {

  public static final String WALA_REPORT = "WALA_report"; //$NON-NLS-1$

  public static final String INPUT_DIR = "input_dir"; //$NON-NLS-1$

  public static final String OUTPUT_DIR = "output_dir"; //$NON-NLS-1$

  public final static String J2SE_DIR = "java_runtime_dir"; //$NON-NLS-1$

  public final static String J2EE_DIR = "j2ee_runtime_dir"; //$NON-NLS-1$

  public final static String ECLIPSE_PLUGINS_DIR = "eclipse_plugins_dir"; //$NON-NLS-1$

  /**
   * Determine the classpath noted in wala.properties for J2SE standard libraries
   * @throws IllegalStateException if there's a problem loading the wala properties
   */
  public static String[] getJ2SEJarFiles() {
    Properties p = null;
    try {
      p = WalaProperties.loadProperties();
    } catch (WalaException e) {
      e.printStackTrace();
      throw new IllegalStateException("problem loading wala.properties");
    String dir = p.getProperty(WalaProperties.J2SE_DIR);
    Assertions.productionAssertion(dir != null);
    return getJarsInDirectory(dir);
  }

  /**
   * @return names of the Jar files holding J2EE libraries
   * @throws IllegalStateException if the J2EE_DIR property is not set
   */
  public static String[] getJ2EEJarFiles() {
    Properties p = null;
    try {
      p = WalaProperties.loadProperties();
    } catch (WalaException e) {
      e.printStackTrace();
      throw new IllegalStateException("problem loading wala.properties");
    }
    String dir = p.getProperty(WalaProperties.J2EE_DIR);
    if (dir == null) {
      throw new IllegalStateException("No J2EE directory specified");
    }
    return getJarsInDirectory(dir);
  }

  public static String[] getJarsInDirectory(String dir) {
    File f = new File(dir);
    Assertions.productionAssertion(f.isDirectory(), "not a directory: " + dir);
    Collection col = FileUtil.listFiles(dir, ".*\\.jar$", true);
    String[] result = new String[col.size()];
    int i = 0;
    for (File jarFile : col) {
      result[i++] = jarFile.getAbsolutePath();
    }
    return result;
  }

  final static String PROPERTY_FILENAME = "wala.properties"; //$NON-NLS-1$

  public static Properties loadProperties() throws WalaException {
    try {
      Properties result = loadPropertiesFromFile(WalaProperties.class.getClassLoader(), PROPERTY_FILENAME);

      String outputDir = result.getProperty(OUTPUT_DIR, DefaultPropertiesValues.DEFAULT_OUTPUT_DIR);
      result.setProperty(OUTPUT_DIR, convertToAbsolute(outputDir));

      String walaReport = result.getProperty(WALA_REPORT, DefaultPropertiesValues.DEFAULT_WALA_REPORT_FILENAME);
      result.setProperty(WALA_REPORT, convertToAbsolute(walaReport));

      return result;
    } catch (Exception e) {
      e.printStackTrace();
      throw new WalaException("Unable to set up wala properties ", e);
    }
  }

  static String convertToAbsolute(String path) {
    final File file = new File(path);
    return (file.isAbsolute()) ? file.getAbsolutePath() : WalaProperties.getWalaHomeDir().concat(File.separator).concat(path);
  }

  public static Properties loadPropertiesFromFile(ClassLoader loader, String fileName) throws IOException {
    if (loader == null) {
      throw new IllegalArgumentException("loader is null");
    }
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    final InputStream propertyStream = loader.getResourceAsStream(fileName);
    if (propertyStream == null) {
      throw new IOException("property_file_unreadable " + fileName);
    }
    Properties result = new Properties();
    result.load(propertyStream);
    return result;
  }

  /**
   * @deprecated because when running under eclipse, there may be no such directory.
   * Need to handle that case.
   */
  @Deprecated
  public static String getWalaHomeDir() {
    final String envProperty = System.getProperty("WALA_HOME"); //$NON-NLS-1$
    if (envProperty != null)
      return envProperty;

    final URL url = WalaProperties.class.getClassLoader().getResource("wala.properties"); //$NON-NLS-1$
    if (url == null) {
      return System.getProperty("user.dir"); //$NON-NLS-1$
    } else {
      return new File((new FileProvider()).filePathFromURL(url)).getParentFile().getParentFile().getPath();
    }
  }
  
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.properties;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Properties;

import com.ibm.wala.util.WalaException;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.io.FileProvider;
import com.ibm.wala.util.io.FileUtil;

public final class WalaProperties {

  public static final String WALA_REPORT = "WALA_report"; //$NON-NLS-1$

  public static final String INPUT_DIR = "input_dir"; //$NON-NLS-1$

  public static final String OUTPUT_DIR = "output_dir"; //$NON-NLS-1$

  public final static String J2SE_DIR = "java_runtime_dir"; //$NON-NLS-1$

  public final static String J2EE_DIR = "j2ee_runtime_dir"; //$NON-NLS-1$

  public final static String ECLIPSE_PLUGINS_DIR = "eclipse_plugins_dir"; //$NON-NLS-1$

  /**
   * Determine the classpath noted in wala.properties for J2SE standard libraries
   * @throws IllegalStateException if there's a problem loading the wala properties
   */
  public static String[] getJ2SEJarFiles() {
    Properties p = null;
    try {
      p = WalaProperties.loadProperties();
    } catch (WalaException e) {
      e.printStackTrace();
      throw new IllegalStateException("problem loading wala.properties");
    }

    String dir = p.getProperty(WalaProperties.J2SE_DIR);
    Assertions.productionAssertion(dir != null);
    return getJarsInDirectory(dir);
  }

  /**
   * @return names of the Jar files holding J2EE libraries
   * @throws IllegalStateException if the J2EE_DIR property is not set
   */
  public static String[] getJ2EEJarFiles() {
    Properties p = null;
    try {
      p = WalaProperties.loadProperties();
    } catch (WalaException e) {
      e.printStackTrace();
      throw new IllegalStateException("problem loading wala.properties");
    }
    String dir = p.getProperty(WalaProperties.J2EE_DIR);
    if (dir == null) {
      throw new IllegalStateException("No J2EE directory specified");
    }
    return getJarsInDirectory(dir);
  }

  public static String[] getJarsInDirectory(String dir) {
    File f = new File(dir);
    Assertions.productionAssertion(f.isDirectory(), "not a directory: " + dir);
    Collection col = FileUtil.listFiles(dir, ".*\\.jar$", true);
    String[] result = new String[col.size()];
    int i = 0;
    for (File jarFile : col) {
      result[i++] = jarFile.getAbsolutePath();
    }
    return result;
  }

  final static String PROPERTY_FILENAME = "wala.properties"; //$NON-NLS-1$

  public static Properties loadProperties() throws WalaException {
    try {
      Properties result = loadPropertiesFromFile(WalaProperties.class.getClassLoader(), PROPERTY_FILENAME);

      String outputDir = result.getProperty(OUTPUT_DIR, DefaultPropertiesValues.DEFAULT_OUTPUT_DIR);
      result.setProperty(OUTPUT_DIR, convertToAbsolute(outputDir));

      String walaReport = result.getProperty(WALA_REPORT, DefaultPropertiesValues.DEFAULT_WALA_REPORT_FILENAME);
      result.setProperty(WALA_REPORT, convertToAbsolute(walaReport));

      return result;
    } catch (Exception e) {
      e.printStackTrace();
      throw new WalaException("Unable to set up wala properties ", e);
    }
  }

  static String convertToAbsolute(String path) {
    final File file = new File(path);
    return (file.isAbsolute()) ? file.getAbsolutePath() : WalaProperties.getWalaHomeDir().concat(File.separator).concat(path);
  }

  public static Properties loadPropertiesFromFile(ClassLoader loader, String fileName) throws IOException {
    if (loader == null) {
      throw new IllegalArgumentException("loader is null");
    }
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    final InputStream propertyStream = loader.getResourceAsStream(fileName);
    if (propertyStream == null) {
/** BEGIN Custom change: create default properties if no file exists */
      // create default properties
      Properties defprop = new Properties();
      defprop.setProperty(OUTPUT_DIR, "./out");
      defprop.setProperty(INPUT_DIR, "./in");
      defprop.setProperty(ECLIPSE_PLUGINS_DIR, "./plugins");
      defprop.setProperty(WALA_REPORT, "./wala_report.txt");
      defprop.setProperty(J2EE_DIR, "./j2ee");
      final String j2selib = guessJavaLib();
      defprop.setProperty(J2SE_DIR, j2selib);
      
      return defprop;
/** END Custom change: create default properties if no file exists */
    }
    Properties result = new Properties();
    result.load(propertyStream);
    
    if (!result.containsKey(J2SE_DIR)) {
      final String j2selib = guessJavaLib();
      result.setProperty(J2SE_DIR, j2selib);
    }
    return result;
  }

/** BEGIN Custom change: create default properties if no file exists */
  public static String guessJavaLib() {
    final Properties p = System.getProperties();
    final String os = p.getProperty("os.name");
        
    if (os.contains("Mac OS X")) {
      return "/System/Library/Frameworks/JavaVM.framework/Classes";
    } else {
      final String home = System.getProperty("java.home");
      return home + File.separator + "lib";
    }
  }
  
/** END Custom change: create default properties if no file exists */
  /**
   * @deprecated because when running under eclipse, there may be no such directory.
   * Need to handle that case.
   */
  @Deprecated
  public static String getWalaHomeDir() {
    final String envProperty = System.getProperty("WALA_HOME"); //$NON-NLS-1$
    if (envProperty != null)
      return envProperty;

    final URL url = WalaProperties.class.getClassLoader().getResource("wala.properties"); //$NON-NLS-1$
    if (url == null) {
      return System.getProperty("user.dir"); //$NON-NLS-1$
    } else {
      return new File((new FileProvider()).filePathFromURL(url)).getParentFile().getParentFile().getPath();
    }
  }
  
}
File
WalaProperties.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
   */
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * A value generated by a phi instruction.
 * 
 * Clients shouldn't use this ... it's only used internally during SSA construction.
 */
public class PhiValue implements Value {
  /**
   * The phi instruction that defines this value
   */
  final private SSAPhiInstruction phi;

  /**
   * @param phi The phi instruction that defines this value
   */
  PhiValue(SSAPhiInstruction phi) {
    this.phi = phi;
  }

  @Override
  public String toString() {
    return "v" + phi.getDef();
  }

  /**
   * @return The phi instruction that defines this value
   */
  public SSAPhiInstruction getPhiInstruction() {
    return phi;
  }

  /*
   * @see com.ibm.wala.ssa.Value#isStringConstant()
   */
  public boolean isStringConstant() {
    return false;
  }

  /*
   * @see com.ibm.wala.ssa.Value#isNullConstant()
   */
  public boolean isNullConstant() {
    return false;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * A value generated by a phi instruction.
 * 
 * Clients shouldn't use this ... it's only used internally during SSA construction.
 */
class PhiValue implements Value {
  /**
   * The phi instruction that defines this value
   */
  final private SSAPhiInstruction phi;

  /**
   * @param phi The phi instruction that defines this value
   */
  PhiValue(SSAPhiInstruction phi) {
    this.phi = phi;
  }

  @Override
  public String toString() {
    return "v" + phi.getDef();
  }

  /**
   * @return The phi instruction that defines this value
   */
  public SSAPhiInstruction getPhiInstruction() {
    return phi;
  }

  /*
   * @see com.ibm.wala.ssa.Value#isStringConstant()
   */
  public boolean isStringConstant() {
    return false;
  }

  /*
   * @see com.ibm.wala.ssa.Value#isNullConstant()
  public boolean isNullConstant() {
    return false;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * A value generated by a phi instruction.
 * 
 * Clients shouldn't use this ... it's only used internally during SSA construction.
 */
public class PhiValue implements Value {
  /**
   * The phi instruction that defines this value
   */
  final private SSAPhiInstruction phi;

  /**
   * @param phi The phi instruction that defines this value
   */
  PhiValue(SSAPhiInstruction phi) {
    this.phi = phi;
  }

  @Override
  public String toString() {
    return "v" + phi.getDef();
  }

  /**
   * @return The phi instruction that defines this value
   */
  public SSAPhiInstruction getPhiInstruction() {
    return phi;
  }

  /*
   * @see com.ibm.wala.ssa.Value#isStringConstant()
   */
  public boolean isStringConstant() {
    return false;
  }

  /*
   * @see com.ibm.wala.ssa.Value#isNullConstant()
   */
  public boolean isNullConstant() {
    return false;
  }

}
File
PhiValue.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Package declaration
Chunk
Conflicting content
  @Override
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * TODO: document me.
 */
public abstract class ReflectiveMemberAccess extends SSAInstruction {
  protected final int objectRef;

  protected final int memberRef;

  protected ReflectiveMemberAccess(int index, int objectRef, int memberRef) {
    super(index);
    this.objectRef = objectRef;
    this.memberRef = memberRef;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "fieldref " + getValueString(symbolTable, objectRef) + "." + getValueString(symbolTable, memberRef);
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? objectRef : memberRef;
  }

  public int getObjectRef() {
    return objectRef;
  }

  public int getMemberRef() {
    return memberRef;
  }

  @Override
  public int hashCode() {
    return 6311 * memberRef ^ 2371 * objectRef;
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#isFallThrough()
   */
  public boolean isFallThrough() {
    return true;
  }

=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * TODO: document me.
 */
public abstract class ReflectiveMemberAccess extends SSAInstruction {
  protected final int objectRef;

  protected final int memberRef;

  protected ReflectiveMemberAccess(int objectRef, int memberRef) {
    super();
    this.objectRef = objectRef;
    this.memberRef = memberRef;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "fieldref " + getValueString(symbolTable, objectRef) + "." + getValueString(symbolTable, memberRef);
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? objectRef : memberRef;
  }

  public int getObjectRef() {
    return objectRef;
  }

  public int getMemberRef() {
    return memberRef;
  }

  @Override
  public int hashCode() {
    return 6311 * memberRef ^ 2371 * objectRef;
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
}
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * TODO: document me.
 */
public abstract class ReflectiveMemberAccess extends SSAInstruction {
  protected final int objectRef;

  protected final int memberRef;

  protected ReflectiveMemberAccess(int index, int objectRef, int memberRef) {
    super(index);
    this.objectRef = objectRef;
    this.memberRef = memberRef;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "fieldref " + getValueString(symbolTable, objectRef) + "." + getValueString(symbolTable, memberRef);
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? objectRef : memberRef;
  }

  public int getObjectRef() {
    return objectRef;
  }

  public int getMemberRef() {
    return memberRef;
  }

  @Override
  public int hashCode() {
    return 6311 * memberRef ^ 2371 * objectRef;
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
File
ReflectiveMemberAccess.java
Developer's decision
Version 1
Kind of conflict
Annotation
Attribute
Comment
Method declaration
Package declaration
Chunk
Conflicting content
  }
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;

/**
 * A Call instruction.
 * 
 * Note that different languages have different notions of what a call is. This is an abstract superclass which encapsulates the
 * common functionality that all languages share, so far.
 */
public abstract class SSAAbstractInvokeInstruction extends SSAInstruction {

  /**
   * The value number which represents the exception object which the call may throw.
   */
  protected final int exception;

  /**
   * The call site, containing the program counter location and the method being called.
   */
  protected final CallSiteReference site;

  /**
   * @param exception The value number which represents the exception object which the call may throw.
   * @param site The call site, containing the program counter location and the method being called.
   */
  protected SSAAbstractInvokeInstruction(int index, int exception, CallSiteReference site) {
    super(index);
    this.exception = exception;
    this.site = site;
  }

  /**
   * @return The call site, containing the program counter location and the method being called.
   */
  public CallSiteReference getCallSite() {
    return site;
  }

  /**
   * Is this a 'static' call? (invokestatic in Java)
   */
  public boolean isStatic() {
    return getCallSite().isStatic();
  }

  /**
   * Might this call dispatch to one of several possible methods?  i.e., in Java, is it an invokeinterface or invokevirtual
   */
  public boolean isDispatch() {
    return getCallSite().isDispatch();
  }

  /**
   * Is this a 'special' call? (invokespecial in Java)
   */
  public boolean isSpecial() {
    return getCallSite().isSpecial();
  }

  /**
   * @return the value number of the receiver of a virtual call
   */
  public int getReceiver() {
    assert site.getInvocationCode() != IInvokeInstruction.Dispatch.STATIC : toString();
    return getUse(0);
  /**

  /**
   * @return the program counter (index into the method's bytecode) for this call site.
   */
  public int getProgramCounter() {
    return site.getProgramCounter();
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfDefs()
   */
  @Override
  public int getNumberOfDefs() {
    return getNumberOfReturnValues() + 1;
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#getDef(int)
   */
  @Override
  public int getDef(int i) {
    if (getNumberOfReturnValues() == 0) {
      assert i == 0;
      return exception;
    } else {
      if (i == 0) {
        return getReturnValue(0);
      } else if (i == 1) {
        return exception;
      } else {
        return getReturnValue(i - 1);
      }
    }
  }

  /**
   * Return the value number which is def'fed by this call instruction if the call returns exceptionally.
   */
  public int getException() {
    return exception;
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#hasDef()
   */
  @Override
  public boolean hasDef() {
    return getNumberOfReturnValues() > 0;
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public int getDef() {
    return getReturnValue(0);
  }

  /**
   * How many parameters does this call specify?
   */
  public abstract int getNumberOfParameters();

  /**
   * How many distinct values does this call return?
   */
  public abstract int getNumberOfReturnValues();

  /**
   * What is the the value number of the ith value returned by this call
   */
  public abstract int getReturnValue(int i);

  /**
   * What is the declared return type of the called method
   */
  public TypeReference getDeclaredResultType() {
    return site.getDeclaredTarget().getReturnType();
  }

  /**
   * What method is the declared callee?
   */
  public MethodReference getDeclaredTarget() {
    return site.getDeclaredTarget();
  }

  /**
   * @see com.ibm.wala.classLoader.CallSiteReference#getInvocationCode()
   */
  public IInvokeInstruction.IDispatch getInvocationCode() {
    return site.getInvocationCode();
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    String code = site.getInvocationString();
    StringBuffer s = new StringBuffer();
    if (hasDef()) {
      s.append(getValueString(symbolTable, getDef())).append(" = ");
    }
    s.append("invoke").append(code);
    s.append(" ");
    s.append(site.getDeclaredTarget().toString());

    if (getNumberOfParameters() > 0) {
      s.append(" ").append(getValueString(symbolTable, getUse(0)));
      for (int i = 1; i < getNumberOfParameters(); i++) {
        s.append(",").append(getValueString(symbolTable, getUse(i)));
      }
    }

    s.append(" @");
    s.append(site.getProgramCounter());

    if (exception == -1) {
      s.append(" exception: NOT MODELED");
    } else {
      s.append(" exception:").append(getValueString(symbolTable, exception));
    }

    return s.toString();
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;

/**
 * A Call instruction.
 * 
 * Note that different languages have different notions of what a call is. This is an abstract superclass which encapsulates the
 * common functionality that all languages share, so far.
 */
public abstract class SSAAbstractInvokeInstruction extends SSAInstruction {

  /**
   * The value number which represents the exception object which the call may throw.
   */
  protected final int exception;

  /**
   * The call site, containing the program counter location and the method being called.
   */
  protected final CallSiteReference site;

   * @param exception The value number which represents the exception object which the call may throw.
   * @param site The call site, containing the program counter location and the method being called.
   */
  protected SSAAbstractInvokeInstruction(int exception, CallSiteReference site) {
    this.exception = exception;
    this.site = site;
  }

  /**
   * @return The call site, containing the program counter location and the method being called.
   */
  public CallSiteReference getCallSite() {
    return site;
  }

  /**
   * Is this a 'static' call? (invokestatic in Java)
   */
  public boolean isStatic() {
    return getCallSite().isStatic();
  }

  /**
   * Might this call dispatch to one of several possible methods?  i.e., in Java, is it an invokeinterface or invokevirtual
   */
  public boolean isDispatch() {
    return getCallSite().isDispatch();
  }

  /**
   * Is this a 'special' call? (invokespecial in Java)
   */
  public boolean isSpecial() {
    return getCallSite().isSpecial();
  }

  /**
   * @return the value number of the receiver of a virtual call
   */
  public int getReceiver() {
    assert site.getInvocationCode() != IInvokeInstruction.Dispatch.STATIC : toString();
    return getUse(0);
  }

  /**
   * @return the program counter (index into the method's bytecode) for this call site.
   */
  public int getProgramCounter() {
    return site.getProgramCounter();
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfDefs()
   */
  @Override
  public int getNumberOfDefs() {
    return getNumberOfReturnValues() + 1;
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#getDef(int)
   */
  @Override
  public int getDef(int i) {
    if (getNumberOfReturnValues() == 0) {
      assert i == 0;
      return exception;
    } else {
      if (i == 0) {
        return getReturnValue(0);
      } else if (i == 1) {
        return exception;
      } else {
        return getReturnValue(i - 1);
      }
    }
  }

  /**
   * Return the value number which is def'fed by this call instruction if the call returns exceptionally.
   */
  public int getException() {
    return exception;
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#hasDef()
   */
  @Override
  public boolean hasDef() {
    return getNumberOfReturnValues() > 0;
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public int getDef() {
    return getReturnValue(0);
  }

  /**
   * How many parameters does this call specify?
   */
  public abstract int getNumberOfParameters();

  /**
   * How many distinct values does this call return?
   */
  public abstract int getNumberOfReturnValues();

  /**
   * What is the the value number of the ith value returned by this call
   */
  public abstract int getReturnValue(int i);

  /**
   * What is the declared return type of the called method
   */
  public TypeReference getDeclaredResultType() {
    return site.getDeclaredTarget().getReturnType();
  }

  /**
   * What method is the declared callee?
   */
  public MethodReference getDeclaredTarget() {
    return site.getDeclaredTarget();
  }

  /**
   * @see com.ibm.wala.classLoader.CallSiteReference#getInvocationCode()
   */
  public IInvokeInstruction.IDispatch getInvocationCode() {
    return site.getInvocationCode();
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    String code = site.getInvocationString();
    StringBuffer s = new StringBuffer();
    if (hasDef()) {
      s.append(getValueString(symbolTable, getDef())).append(" = ");
    }
    s.append("invoke").append(code);
    s.append(" ");
    s.append(site.getDeclaredTarget().toString());

    if (getNumberOfParameters() > 0) {
      s.append(" ").append(getValueString(symbolTable, getUse(0)));
      for (int i = 1; i < getNumberOfParameters(); i++) {
        s.append(",").append(getValueString(symbolTable, getUse(i)));
      }
    }

    s.append(" @");
    s.append(site.getProgramCounter());

    if (exception == -1) {
      s.append(" exception: NOT MODELED");
    } else {
      s.append(" exception:").append(getValueString(symbolTable, exception));
    }

    return s.toString();
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;

/**
 * A Call instruction.
 * 
 * Note that different languages have different notions of what a call is. This is an abstract superclass which encapsulates the
 * common functionality that all languages share, so far.
 */
public abstract class SSAAbstractInvokeInstruction extends SSAInstruction {

  /**
   * The value number which represents the exception object which the call may throw.
   */
  protected final int exception;

  /**
   * The call site, containing the program counter location and the method being called.
   */
  protected final CallSiteReference site;

  /**
   * @param exception The value number which represents the exception object which the call may throw.
   * @param site The call site, containing the program counter location and the method being called.
   */
  protected SSAAbstractInvokeInstruction(int index, int exception, CallSiteReference site) {
    super(index);
    this.exception = exception;
    this.site = site;
  }

  /**
   * @return The call site, containing the program counter location and the method being called.
   */
  public CallSiteReference getCallSite() {
    return site;
  }

  /**
   * Is this a 'static' call? (invokestatic in Java)
   */
  public boolean isStatic() {
    return getCallSite().isStatic();
  }

  /**
   * Might this call dispatch to one of several possible methods?  i.e., in Java, is it an invokeinterface or invokevirtual
   */
  public boolean isDispatch() {
    return getCallSite().isDispatch();
  }

  /**
   * Is this a 'special' call? (invokespecial in Java)
   */
  public boolean isSpecial() {
    return getCallSite().isSpecial();
  }

  /**
   * @return the value number of the receiver of a virtual call
   */
  public int getReceiver() {
    assert site.getInvocationCode() != IInvokeInstruction.Dispatch.STATIC : toString();
    return getUse(0);
  }

  /**
   * @return the program counter (index into the method's bytecode) for this call site.
   */
  public int getProgramCounter() {
    return site.getProgramCounter();
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfDefs()
   */
  @Override
  public int getNumberOfDefs() {
    return getNumberOfReturnValues() + 1;
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#getDef(int)
   */
  @Override
  public int getDef(int i) {
    if (getNumberOfReturnValues() == 0) {
      assert i == 0;
      return exception;
    } else {
      if (i == 0) {
        return getReturnValue(0);
      } else if (i == 1) {
        return exception;
      } else {
        return getReturnValue(i - 1);
      }
    }
  }

  /**
   * Return the value number which is def'fed by this call instruction if the call returns exceptionally.
   */
  public int getException() {
    return exception;
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#hasDef()
   */
  @Override
  public boolean hasDef() {
    return getNumberOfReturnValues() > 0;
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public int getDef() {
    return getReturnValue(0);
  }

  /**
   * How many parameters does this call specify?
   */
  public abstract int getNumberOfParameters();

  /**
   * How many distinct values does this call return?
   */
  public abstract int getNumberOfReturnValues();

  /**
   * What is the the value number of the ith value returned by this call
   */
  public abstract int getReturnValue(int i);

  /**
   * What is the declared return type of the called method
   */
  public TypeReference getDeclaredResultType() {
    return site.getDeclaredTarget().getReturnType();
  }

  /**
   * What method is the declared callee?
   */
  public MethodReference getDeclaredTarget() {
    return site.getDeclaredTarget();
  }

  /**
   * @see com.ibm.wala.classLoader.CallSiteReference#getInvocationCode()
   */
  public IInvokeInstruction.IDispatch getInvocationCode() {
    return site.getInvocationCode();
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    String code = site.getInvocationString();
    StringBuffer s = new StringBuffer();
    if (hasDef()) {
      s.append(getValueString(symbolTable, getDef())).append(" = ");
    }
    s.append("invoke").append(code);
    s.append(" ");
    s.append(site.getDeclaredTarget().toString());

    if (getNumberOfParameters() > 0) {
      s.append(" ").append(getValueString(symbolTable, getUse(0)));
      for (int i = 1; i < getNumberOfParameters(); i++) {
        s.append(",").append(getValueString(symbolTable, getUse(i)));
      }
    }

    s.append(" @");
    s.append(site.getProgramCounter());

    if (exception == -1) {
      s.append(" exception: NOT MODELED");
    } else {
      s.append(" exception:").append(getValueString(symbolTable, exception));
    }

    return s.toString();
  }

}
File
SSAAbstractInvokeInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * An instruction which unconditionally throws an exception
 */
public abstract class SSAAbstractThrowInstruction extends SSAInstruction {
  private final int exception;

  public SSAAbstractThrowInstruction(int index, int exception) {
    super(index);
    //assert exception > 0;
    this.exception = exception;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "throw " + getValueString(symbolTable, exception);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (j != 0) {
      throw new IllegalArgumentException("j must be 0");
    }
    return exception;
  }

  @Override
  public int hashCode() {
    return 7529 + exception * 823;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /* 
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return false;
  }

  /**
   * @return value number of the thrown exception object.
   */
  public int getException() {
    return exception;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * An instruction which unconditionally throws an exception
 */
public abstract class SSAAbstractThrowInstruction extends SSAInstruction {
  private final int exception;

  public SSAAbstractThrowInstruction(int exception) {
    super();
    //assert exception > 0;
    this.exception = exception;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "throw " + getValueString(symbolTable, exception);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (j != 0) {
      throw new IllegalArgumentException("j must be 0");
    }
    return exception;
  }

  @Override
  public int hashCode() {
    return 7529 + exception * 823;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /* 
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return false;
  }

  /**
   * @return value number of the thrown exception object.
   */
  public int getException() {
    return exception;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
  public int getUse(int j) {
    if (j != 0) {
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * An instruction which unconditionally throws an exception
 */
public abstract class SSAAbstractThrowInstruction extends SSAInstruction {
  private final int exception;

  public SSAAbstractThrowInstruction(int index, int exception) {
    super(index);
    //assert exception > 0;
    this.exception = exception;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "throw " + getValueString(symbolTable, exception);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
      throw new IllegalArgumentException("j must be 0");
    }
    return exception;
  }

  @Override
  public int hashCode() {
    return 7529 + exception * 823;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /* 
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return false;
  }

  /**
   * @return value number of the thrown exception object.
   */
  public int getException() {
    return exception;
  }

}
File
SSAAbstractThrowInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 */
public abstract class SSAAbstractUnaryInstruction extends SSAInstruction {

  protected final int result;

  protected final int val;

  protected SSAAbstractUnaryInstruction(int index, int result, int val) {
    super(index);
    this.result = result;
    this.val = val;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return val;
  }

  @Override
  public int hashCode() {
    return val * 1663 ^ result * 4027;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 */
public abstract class SSAAbstractUnaryInstruction extends SSAInstruction {

  protected final int result;

  protected final int val;

  protected SSAAbstractUnaryInstruction(int result, int val) {
    super();
    this.result = result;
    this.val = val;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return val;
  }

  @Override
  public int hashCode() {
    return val * 1663 ^ result * 4027;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 */
public abstract class SSAAbstractUnaryInstruction extends SSAInstruction {

  protected final int result;

  protected final int val;

  protected SSAAbstractUnaryInstruction(int index, int result, int val) {
    super(index);
    this.result = result;
    this.val = val;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return val;
  }
  @Override
  public int hashCode() {
    return val * 1663 ^ result * 4027;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
File
SSAAbstractUnaryInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * SSA instruction representing v_x := arraylength v_y
 */
public abstract class SSAArrayLengthInstruction extends SSAInstruction {
  private final int result;

  private final int arrayref;

  protected SSAArrayLengthInstruction(int index, int result, int arrayref) {
    super(index);
    this.result = result;
    this.arrayref = arrayref;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (defs != null && defs.length != 1) {
      throw new IllegalArgumentException();
    }
    if (uses != null && uses.length != 1) {
      throw new IllegalArgumentException();
    }
    return insts.ArrayLengthInstruction(iindex, defs == null ? result : defs[0], uses == null ? arrayref : uses[0]);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = arraylength " + getValueString(symbolTable, arrayref);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitArrayLength(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public int getDef() {
    return result;
  }

  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef(int i) {
    if (i != 0) {
      throw new IllegalArgumentException("invalid i " + i);
    }
    return result;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  public int getArrayRef() {
    return arrayref;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (j != 0) {
      throw new IllegalArgumentException("invalid j: " + j);
    }
    return arrayref;
  }

  @Override
  public int hashCode() {
    return arrayref * 7573 + result * 563;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * SSA instruction representing v_x := arraylength v_y
 */
public abstract class SSAArrayLengthInstruction extends SSAInstruction {
  private final int result;

  private final int arrayref;

  protected SSAArrayLengthInstruction(int result, int arrayref) {
    super();
    this.result = result;
    this.arrayref = arrayref;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (defs != null && defs.length != 1) {
      throw new IllegalArgumentException();
    }
    if (uses != null && uses.length != 1) {
      throw new IllegalArgumentException();
    }
    return insts.ArrayLengthInstruction(defs == null ? result : defs[0], uses == null ? arrayref : uses[0]);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = arraylength " + getValueString(symbolTable, arrayref);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitArrayLength(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public int getDef() {
    return result;
  }

  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef(int i) {
    if (i != 0) {
      throw new IllegalArgumentException("invalid i " + i);
    }
    return result;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  public int getArrayRef() {
    return arrayref;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (j != 0) {
      throw new IllegalArgumentException("invalid j: " + j);
    }
    return arrayref;
  }

  @Override
  public int hashCode() {
    return arrayref * 7573 + result * 563;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * SSA instruction representing v_x := arraylength v_y
 */
public abstract class SSAArrayLengthInstruction extends SSAInstruction {
  private final int result;

  private final int arrayref;

  protected SSAArrayLengthInstruction(int index, int result, int arrayref) {
    super(index);
    this.result = result;
    this.arrayref = arrayref;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (defs != null && defs.length != 1) {
      throw new IllegalArgumentException();
    }
    if (uses != null && uses.length != 1) {
      throw new IllegalArgumentException();
    }
    return insts.ArrayLengthInstruction(iindex, defs == null ? result : defs[0], uses == null ? arrayref : uses[0]);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = arraylength " + getValueString(symbolTable, arrayref);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitArrayLength(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public int getDef() {
    return result;
  }

  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef(int i) {
    if (i != 0) {
      throw new IllegalArgumentException("invalid i " + i);
    }
    return result;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  public int getArrayRef() {
    return arrayref;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (j != 0) {
      throw new IllegalArgumentException("invalid j: " + j);
    }
    return arrayref;
  }

  @Override
  public int hashCode() {
    return arrayref * 7573 + result * 563;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
File
SSAArrayLengthInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Package declaration
Chunk
Conflicting content
    return 1;
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * SSA instruction representing an array load.
 */
public abstract class SSAArrayLoadInstruction extends SSAArrayReferenceInstruction {
  private final int result;

  protected SSAArrayLoadInstruction(int iindex, int result, int arrayref, int index, TypeReference elementType) {
    super(iindex, arrayref, index, elementType);
    this.result = result;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("defs.length == 0");
    }
    if (uses != null && uses.length < 2) {
      throw new IllegalArgumentException("uses.length < 2");
    }
    return insts.ArrayLoadInstruction(iindex, defs == null ? result : defs[0], uses == null ? getArrayRef() : uses[0],
        uses == null ? getIndex() : uses[1], getElementType());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = arrayload " + getValueString(symbolTable, getArrayRef()) + "["
        + getValueString(symbolTable, getIndex()) + "]";
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException
   *             if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitArrayLoad(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    if (i != 0) {
      throw new IllegalArgumentException("illegal i: " + i);
    }
    return result;
  }

  @Override
  public int getNumberOfDefs() {
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ 2371 * getArrayRef() + getIndex();
  }


}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * SSA instruction representing an array load.
 */
public abstract class SSAArrayLoadInstruction extends SSAArrayReferenceInstruction {
  private final int result;

  protected SSAArrayLoadInstruction(int result, int arrayref, int index, TypeReference elementType) {
    super(arrayref, index, elementType);
    this.result = result;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("defs.length == 0");
    }
    if (uses != null && uses.length < 2) {
      throw new IllegalArgumentException("uses.length < 2");
    }
    return insts.ArrayLoadInstruction(defs == null ? result : defs[0], uses == null ? getArrayRef() : uses[0],
        uses == null ? getIndex() : uses[1], getElementType());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = arrayload " + getValueString(symbolTable, getArrayRef()) + "["
        + getValueString(symbolTable, getIndex()) + "]";
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException
   *             if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitArrayLoad(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    if (i != 0) {
      throw new IllegalArgumentException("illegal i: " + i);
    }
    return result;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ 2371 * getArrayRef() + getIndex();
  }


}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * SSA instruction representing an array load.
 */
public abstract class SSAArrayLoadInstruction extends SSAArrayReferenceInstruction {
  private final int result;

  protected SSAArrayLoadInstruction(int iindex, int result, int arrayref, int index, TypeReference elementType) {
    super(iindex, arrayref, index, elementType);
    this.result = result;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("defs.length == 0");
    }
    if (uses != null && uses.length < 2) {
      throw new IllegalArgumentException("uses.length < 2");
    }
    return insts.ArrayLoadInstruction(iindex, defs == null ? result : defs[0], uses == null ? getArrayRef() : uses[0],
        uses == null ? getIndex() : uses[1], getElementType());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = arrayload " + getValueString(symbolTable, getArrayRef()) + "["
        + getValueString(symbolTable, getIndex()) + "]";
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException
   *             if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitArrayLoad(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    if (i != 0) {
      throw new IllegalArgumentException("illegal i: " + i);
    }
    return result;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ 2371 * getArrayRef() + getIndex();
  }


}
File
SSAArrayLoadInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * Abstract base class for instructions that load or store from array contents.
 */
public abstract class SSAArrayReferenceInstruction extends SSAInstruction {

  private final int arrayref;

  private final int idx;

  private final TypeReference elementType;

  SSAArrayReferenceInstruction(int iindex, int arrayref, int index, TypeReference elementType) {
    super(iindex);
    this.arrayref = arrayref;
    this.idx = index;
    this.elementType = elementType;
    if (elementType == null) {
      throw new IllegalArgumentException("null elementType");
    }
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? arrayref : idx;
  }

  /**
   * Return the value number of the array reference.
   */
  public int getArrayRef() {
    return arrayref;
  }

  /**
   * Return the value number of the index of the array reference.
   */
  public int getIndex() {
    return idx;
  }

  public TypeReference getElementType() {
    return elementType;
  }

  /**
   * @return true iff this represents an array access of a primitive type element
   */
  public boolean typeIsPrimitive() {
    return elementType.isPrimitiveType();
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * Abstract base class for instructions that load or store from array contents.
 */
public abstract class SSAArrayReferenceInstruction extends SSAInstruction {

  private final int arrayref;

  private final int index;

  private final TypeReference elementType;

  SSAArrayReferenceInstruction(int arrayref, int index, TypeReference elementType) {
    this.arrayref = arrayref;
    this.index = index;
    this.elementType = elementType;
    if (elementType == null) {
      throw new IllegalArgumentException("null elementType");
    }
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? arrayref : index;
  }

  /**
   * Return the value number of the array reference.
   */
  public int getArrayRef() {
    return arrayref;
  }

  /**
   * Return the value number of the index of the array reference.
   */
  public int getIndex() {
    return index;
  }

  public TypeReference getElementType() {
    return elementType;
  }

  /**
   * @return true iff this represents an array access of a primitive type element
   */
  public boolean typeIsPrimitive() {
    return elementType.isPrimitiveType();
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * Abstract base class for instructions that load or store from array contents.
 */
public abstract class SSAArrayReferenceInstruction extends SSAInstruction {

  private final int arrayref;

  private final int idx;

  private final TypeReference elementType;

  SSAArrayReferenceInstruction(int iindex, int arrayref, int index, TypeReference elementType) {
    super(iindex);
    this.arrayref = arrayref;
    this.idx = index;
    this.elementType = elementType;
    if (elementType == null) {
      throw new IllegalArgumentException("null elementType");
    }
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? arrayref : idx;
  }

  /**
   * Return the value number of the array reference.
   */
  public int getArrayRef() {
    return arrayref;
  }

  /**
   * Return the value number of the index of the array reference.
   */
  public int getIndex() {
    return idx;
  }

  public TypeReference getElementType() {
    return elementType;
  }

  /**
   * @return true iff this represents an array access of a primitive type element
   */
  public boolean typeIsPrimitive() {
    return elementType.isPrimitiveType();
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
File
SSAArrayReferenceInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * SSA instruction representing an array store.
 */
public abstract class SSAArrayStoreInstruction extends SSAArrayReferenceInstruction {

  private final int value;

  protected SSAArrayStoreInstruction(int iindex, int arrayref, int index, int value, TypeReference elementType) {
    super(iindex, arrayref, index, elementType);
    this.value = value;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (uses != null && uses.length < 3) {
      throw new IllegalArgumentException("uses.length < 3");
    }
    return insts.ArrayStoreInstruction(iindex, uses == null ? getArrayRef() : uses[0], uses == null ? getIndex() : uses[1],
        uses == null ? value : uses[2], getElementType());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "arraystore " + getValueString(symbolTable, getArrayRef()) + "[" + getValueString(symbolTable, getIndex())
        + "] = " + getValueString(symbolTable, value);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException
   *             if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitArrayStore(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 3;
  }

  @Override
  public int getNumberOfDefs() {
    return 0;
  }

  public int getValue() {
    return value;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (j == 2)
      return value;
    else
      return super.getUse(j);
  }

  @Override
  public int hashCode() {
    return 6311 * value ^ 2371 * getArrayRef() + getIndex();
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * SSA instruction representing an array store.
 */
public abstract class SSAArrayStoreInstruction extends SSAArrayReferenceInstruction {

  private final int value;

  protected SSAArrayStoreInstruction(int arrayref, int index, int value, TypeReference elementType) {
    super(arrayref, index, elementType);
    this.value = value;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (uses != null && uses.length < 3) {
      throw new IllegalArgumentException("uses.length < 3");
    }
    return insts.ArrayStoreInstruction(uses == null ? getArrayRef() : uses[0], uses == null ? getIndex() : uses[1],
        uses == null ? value : uses[2], getElementType());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "arraystore " + getValueString(symbolTable, getArrayRef()) + "[" + getValueString(symbolTable, getIndex())
        + "] = " + getValueString(symbolTable, value);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException
   *             if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitArrayStore(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 3;
  }

  @Override
  public int getNumberOfDefs() {
    return 0;
  }

  public int getValue() {
    return value;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (j == 2)
      return value;
    else
      return super.getUse(j);
  }

  @Override
  public int hashCode() {
    return 6311 * value ^ 2371 * getArrayRef() + getIndex();
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * SSA instruction representing an array store.
 */
public abstract class SSAArrayStoreInstruction extends SSAArrayReferenceInstruction {

  private final int value;

  protected SSAArrayStoreInstruction(int iindex, int arrayref, int index, int value, TypeReference elementType) {
    super(iindex, arrayref, index, elementType);
    this.value = value;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (uses != null && uses.length < 3) {
      throw new IllegalArgumentException("uses.length < 3");
    }
    return insts.ArrayStoreInstruction(iindex, uses == null ? getArrayRef() : uses[0], uses == null ? getIndex() : uses[1],
        uses == null ? value : uses[2], getElementType());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "arraystore " + getValueString(symbolTable, getArrayRef()) + "[" + getValueString(symbolTable, getIndex())
        + "] = " + getValueString(symbolTable, value);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException
   *             if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitArrayStore(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 3;
  }

  @Override
  public int getNumberOfDefs() {
    return 0;
  }

  public int getValue() {
    return value;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (j == 2)
      return value;
    else
      return super.getUse(j);
  }

  @Override
  public int hashCode() {
    return 6311 * value ^ 2371 * getArrayRef() + getIndex();
  }

}
File
SSAArrayStoreInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
    super(index);
  @Override
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.BinaryOpInstruction;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;

public abstract class SSABinaryOpInstruction extends SSAInstruction {

  private final int result;

  private final int val1;

  private final int val2;

  private final IBinaryOpInstruction.IOperator operator;

  /**
   * Might this instruction represent integer arithmetic?
   */
  private final boolean mayBeInteger;

  protected SSABinaryOpInstruction(int index, IBinaryOpInstruction.IOperator operator, int result, int val1, int val2, boolean mayBeInteger) {
    this.result = result;
    this.val1 = val1;
    this.val2 = val2;
    this.operator = operator;
    this.mayBeInteger = mayBeInteger;
    if (val1 <= 0) {
      throw new IllegalArgumentException("illegal val1: " + val1);
    }
    if (val2 <= 0) {
      throw new IllegalArgumentException("illegal val2: " + val2);
    }
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = binaryop(" + operator + ") " + getValueString(symbolTable, val1) + " , "
        + getValueString(symbolTable, val2);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitBinaryOp(this);
  }

  /**
   * Ugh. clean up shrike operator stuff.
   */
  public IBinaryOpInstruction.IOperator getOperator() {
    return operator;
  }

  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? val1 : val2;
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ 2371 * val1 + val2;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return mayBeInteger && (operator == BinaryOpInstruction.Operator.DIV || operator == BinaryOpInstruction.Operator.REM);
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  public boolean mayBeIntegerOp() {
    return mayBeInteger;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.BinaryOpInstruction;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;

public abstract class SSABinaryOpInstruction extends SSAInstruction {

  private final int result;

  private final int val1;

  private final int val2;

  private final IBinaryOpInstruction.IOperator operator;

  /**
   * Might this instruction represent integer arithmetic?
   */
  private final boolean mayBeInteger;

  protected SSABinaryOpInstruction(IBinaryOpInstruction.IOperator operator, int result, int val1, int val2, boolean mayBeInteger) {
    super();
    this.result = result;
    this.val1 = val1;
    this.val2 = val2;
    this.operator = operator;
    this.mayBeInteger = mayBeInteger;
    if (val1 <= 0) {
      throw new IllegalArgumentException("illegal val1: " + val1);
    }
    if (val2 <= 0) {
      throw new IllegalArgumentException("illegal val2: " + val2);
    }
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = binaryop(" + operator + ") " + getValueString(symbolTable, val1) + " , "
        + getValueString(symbolTable, val2);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitBinaryOp(this);
  }

  /**
   * Ugh. clean up shrike operator stuff.
   */
  public IBinaryOpInstruction.IOperator getOperator() {
    return operator;
  }

  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? val1 : val2;
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ 2371 * val1 + val2;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return mayBeInteger && (operator == BinaryOpInstruction.Operator.DIV || operator == BinaryOpInstruction.Operator.REM);
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  public boolean mayBeIntegerOp() {
    return mayBeInteger;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
  @Override
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.BinaryOpInstruction;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;

public abstract class SSABinaryOpInstruction extends SSAInstruction {

  private final int result;

  private final int val1;

  private final int val2;

  private final IBinaryOpInstruction.IOperator operator;

  /**
   * Might this instruction represent integer arithmetic?
   */
  private final boolean mayBeInteger;

  protected SSABinaryOpInstruction(int index, IBinaryOpInstruction.IOperator operator, int result, int val1, int val2, boolean mayBeInteger) {
    super(index);
    this.result = result;
    this.val1 = val1;
    this.val2 = val2;
    this.operator = operator;
    this.mayBeInteger = mayBeInteger;
    if (val1 <= 0) {
      throw new IllegalArgumentException("illegal val1: " + val1);
    }
    if (val2 <= 0) {
      throw new IllegalArgumentException("illegal val2: " + val2);
    }
  }
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = binaryop(" + operator + ") " + getValueString(symbolTable, val1) + " , "
        + getValueString(symbolTable, val2);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitBinaryOp(this);
  }

  /**
   * Ugh. clean up shrike operator stuff.
   */
  public IBinaryOpInstruction.IOperator getOperator() {
    return operator;
  }

  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? val1 : val2;
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ 2371 * val1 + val2;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return mayBeInteger && (operator == BinaryOpInstruction.Operator.DIV || operator == BinaryOpInstruction.Operator.REM);
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  public boolean mayBeIntegerOp() {
    return mayBeInteger;
  }
}
File
SSABinaryOpInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import java.util.Iterator;

import com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.cfg.ShrikeCFG;
import com.ibm.wala.cfg.ShrikeCFG.BasicBlock;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IBytecodeMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.shrikeBT.ArrayLengthInstruction;
import com.ibm.wala.shrikeBT.ConstantInstruction;
import com.ibm.wala.shrikeBT.GotoInstruction;
import com.ibm.wala.shrikeBT.IArrayLoadInstruction;
import com.ibm.wala.shrikeBT.IArrayStoreInstruction;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IComparisonInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IConversionInstruction;
import com.ibm.wala.shrikeBT.IGetInstruction;
import com.ibm.wala.shrikeBT.IInstanceofInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
      /**
import com.ibm.wala.shrikeBT.ILoadIndirectInstruction;
import com.ibm.wala.shrikeBT.ILoadInstruction;
import com.ibm.wala.shrikeBT.IPutInstruction;
import com.ibm.wala.shrikeBT.IShiftInstruction;
import com.ibm.wala.shrikeBT.IStoreIndirectInstruction;
import com.ibm.wala.shrikeBT.IStoreInstruction;
import com.ibm.wala.shrikeBT.ITypeTestInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.IndirectionData;
import com.ibm.wala.shrikeBT.MonitorInstruction;
import com.ibm.wala.shrikeBT.NewInstruction;
import com.ibm.wala.shrikeBT.ReturnInstruction;
import com.ibm.wala.shrikeBT.SwitchInstruction;
import com.ibm.wala.shrikeBT.ThrowInstruction;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.ShrikeIndirectionData.ShrikeLocalName;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.dominators.Dominators;
import com.ibm.wala.util.intset.IntPair;
import com.ibm.wala.util.shrike.ShrikeUtil;

/**
 * This class constructs an SSA {@link IR} from a backing ShrikeBT instruction stream.
 * 
 * The basic algorithm here is an abstract interpretation over the Java bytecode to determine types of stack locations and local
 * variables. As a side effect, the flow functions of the abstract interpretation emit instructions, eliminating the stack
 * abstraction and moving to a register-transfer language in SSA form.
 */
public class SSABuilder extends AbstractIntStackMachine {

  public static SSABuilder make(IBytecodeMethod method, SSACFG cfg, ShrikeCFG scfg, SSAInstruction[] instructions,
      SymbolTable symbolTable, boolean buildLocalMap, SSAPiNodePolicy piNodePolicy) throws IllegalArgumentException {
    if (scfg == null) {
      throw new IllegalArgumentException("scfg == null");
    }
    return new SSABuilder(method, cfg, scfg, instructions, symbolTable, buildLocalMap, piNodePolicy);
  }

  /**
   * A wrapper around the method being analyzed.
   */
  final private IBytecodeMethod method;

  /**
   * Governing symbol table
   */
  final private SymbolTable symbolTable;

  /**

   * A logical mapping from  -> local number if null, don't build it.
   */
  private final SSA2LocalMap localMap;

  /**
   * a factory to create concrete instructions
   */
  private final SSAInstructionFactory insts;

  /**
   * information about indirect use of local variables in the bytecode
   */
  private final IndirectionData bytecodeIndirections;
  
  private final ShrikeIndirectionData ssaIndirections;
  
  private SSABuilder(IBytecodeMethod method, SSACFG cfg, ShrikeCFG scfg, SSAInstruction[] instructions, SymbolTable symbolTable,
      boolean buildLocalMap, SSAPiNodePolicy piNodePolicy) {
    super(scfg);
    localMap = buildLocalMap ? new SSA2LocalMap(scfg, instructions.length, cfg.getNumberOfNodes()) : null;
    init(new SymbolTableMeeter(symbolTable, cfg, scfg), new SymbolicPropagator(scfg, instructions, symbolTable,
        localMap, cfg, piNodePolicy));
    this.method = method;
    this.symbolTable = symbolTable;
    this.insts = method.getDeclaringClass().getClassLoader().getInstructionFactory();
    this.bytecodeIndirections = method.getIndirectionData();
    this.ssaIndirections = new ShrikeIndirectionData(instructions.length);
    assert cfg != null : "Null CFG";
  }

  private class SymbolTableMeeter implements Meeter {

    final SSACFG cfg;


    final SymbolTable symbolTable;

    final ShrikeCFG shrikeCFG;

    SymbolTableMeeter(SymbolTable symbolTable, SSACFG cfg, ShrikeCFG shrikeCFG) {
      this.cfg = cfg;
      this.symbolTable = symbolTable;
      this.shrikeCFG = shrikeCFG;
    }

    public int meetStack(int slot, int[] rhs, BasicBlock bb) {

      assert bb != null : "null basic block";

      if (bb.isExitBlock()) {
        return TOP;
      }

      if (allTheSame(rhs)) {
        for (int i = 0; i < rhs.length; i++) {
          if (rhs[i] != TOP) {
            return rhs[i];
          }
        }
        // didn't find anything but TOP
        return TOP;
      } else {
        SSACFG.BasicBlock newBB = cfg.getNode(shrikeCFG.getNumber(bb));
        // if we already have a phi for this stack location
        SSAPhiInstruction phi = newBB.getPhiForStackSlot(slot);
        int result;
        if (phi == null) {
          // no phi already exists. create one.
          result = symbolTable.newPhi(rhs);
          PhiValue v = symbolTable.getPhiValue(result);
          phi = v.getPhiInstruction();
          newBB.addPhiForStackSlot(slot, phi);
        } else {
          // already created a phi. update it to account for the
          // new merge.
          result = phi.getDef();
          phi.setValues(rhs.clone());
        }
        return result;
      }
    }

    /**
     * @see com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine.Meeter#meetLocal(int, int[], BasicBlock)
     */
    public int meetLocal(int n, int[] rhs, BasicBlock bb) {
      if (allTheSame(rhs)) {
        for (int i = 0; i < rhs.length; i++) {
          if (rhs[i] != TOP) {
            return rhs[i];
          }
        }
        // didn't find anything but TOP
        return TOP;
      } else {
        SSACFG.BasicBlock newBB = cfg.getNode(shrikeCFG.getNumber(bb));
        if (bb.isExitBlock()) {
          // no phis in exit block please
          return TOP;
        }
        // if we already have a phi for this local
      }
        SSAPhiInstruction phi = newBB.getPhiForLocal(n);
        int result;
        if (phi == null) {
          // no phi already exists. create one.
          result = symbolTable.newPhi(rhs);
          PhiValue v = symbolTable.getPhiValue(result);
          phi = v.getPhiInstruction();
          newBB.addPhiForLocal(n, phi);
        } else {
          // already created a phi. update it to account for the
          // new merge.
          result = phi.getDef();
          phi.setValues(rhs.clone());
        }
        return result;
      }
    }

    /**
     * Are all rhs values all the same? Note, we consider TOP (-1) to be same as everything else.
     * 
     * @param rhs
     * @return boolean
     */
    private boolean allTheSame(int[] rhs) {
      int x = -1;
      // set x := the first non-TOP value
      int i = 0;
      for (i = 0; i < rhs.length; i++) {
        if (rhs[i] != TOP) {
          x = rhs[i];
          break;
        }
      }
      // check the remaining values
      for (i = i + 1; i < rhs.length; i++) {
        if (rhs[i] != x && rhs[i] != TOP)
          return false;
      }
      return true;
    }

    /**
          if (creators.length < (s.getDef(i) + 1)) {
     * @see com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine.Meeter#meetStackAtCatchBlock(BasicBlock)
     */
    public int meetStackAtCatchBlock(BasicBlock bb) {
      int bbNumber = shrikeCFG.getNumber(bb);
      SSACFG.ExceptionHandlerBasicBlock newBB = (SSACFG.ExceptionHandlerBasicBlock) cfg.getNode(bbNumber);
      SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction();
      int exceptionValue;
      if (s == null) {
        exceptionValue = symbolTable.newSymbol();
        s = insts.GetCaughtExceptionInstruction(SSAInstruction.NO_INDEX, bbNumber, exceptionValue);
        newBB.setCatchInstruction(s);
      } else {
        exceptionValue = s.getException();
      }
      return exceptionValue;

    }
  }

  @Override
  protected void initializeVariables() {
    MachineState entryState = getEntryState();
    int parameterNumber = 0;
    int local = -1;
    for (int i = 0; i < method.getNumberOfParameters(); i++) {
      local++;
      TypeReference t = method.getParameterType(i);
      if (t != null) {
        int symbol = symbolTable.getParameter(parameterNumber++);
        entryState.setLocal(local, symbol);
        if (t.equals(TypeReference.Double) || t.equals(TypeReference.Long)) {
          local++;
        }
      }
    }

    // This useless value ensures that the state cannot be empty, even
    // for a static method with no arguments in blocks with an empty stack
    // and no locals being used. This ensures that propagation of the
    // state thru the CFGSystem will always show changes the first time
    // it reaches a block, and thus no piece of the CFG will be skipped.
    //
    // (note that this bizarre state really happened, in java_cup)
    //
    // SJF: I don't understand how this is supposed to work. It
    // causes a bug right now in normal cases, so I'm commenting it out
    // for now. If there's a problem, let's add a regression test
    // to catch it.
    //
    entryState.push(symbolTable.newSymbol());
  }

  /**
   * This class defines the type abstractions for this analysis and the flow function for each instruction in the ShrikeBT IR.
   */
  private class SymbolicPropagator extends BasicStackFlowProvider {
    final SSAInstruction[] instructions;

    final SymbolTable symbolTable;

    final ShrikeCFG shrikeCFG;

    final SSACFG cfg;

    final ClassLoaderReference loader;

    /**
     * creators[i] holds the instruction that defs value number i
     */
    private SSAInstruction[] creators;

    final SSA2LocalMap localMap;

    final SSAPiNodePolicy piNodePolicy;

    public SymbolicPropagator(ShrikeCFG shrikeCFG, SSAInstruction[] instructions, SymbolTable symbolTable, SSA2LocalMap localMap,
        SSACFG cfg, SSAPiNodePolicy piNodePolicy) {
      super(shrikeCFG);
      this.piNodePolicy = piNodePolicy;
      this.cfg = cfg;
      this.creators = new SSAInstruction[0];
      this.shrikeCFG = shrikeCFG;
      this.instructions = instructions;
      this.symbolTable = symbolTable;
      this.loader = shrikeCFG.getMethod().getDeclaringClass().getClassLoader().getReference();
      this.localMap = localMap;
      init(this.new NodeVisitor(), this.new EdgeVisitor());
    }

    @Override
    public boolean needsEdgeFlow() {
      return piNodePolicy != null;
    }

    private void emitInstruction(SSAInstruction s) {
      if (s != null) {
        instructions[getCurrentInstructionIndex()] = s;
        for (int i = 0; i < s.getNumberOfDefs(); i++) {
            SSAInstruction[] arr = new SSAInstruction[2 * s.getDef(i)];
            System.arraycopy(creators, 0, arr, 0, creators.length);
            creators = arr;
          }

          assert s.getDef(i) != -1 : "invalid def " + i + " for " + s;
        
          creators[s.getDef(i)] = s;
        }
      }
    }

    private SSAInstruction getCurrentInstruction() {
      return instructions[getCurrentInstructionIndex()];
    }

    /**
     * If we've already created the current instruction, return the value number def'ed by the current instruction. Else, create a
     * new symbol.
     */
    private int reuseOrCreateDef() {
      if (getCurrentInstruction() == null) {
        return symbolTable.newSymbol();
      } else {
        return getCurrentInstruction().getDef();
      }
    }

    /**
     * If we've already created the current instruction, return the value number representing the exception the instruction may
     * throw. Else, create a new symbol
     */
    private int reuseOrCreateException() {

      if (getCurrentInstruction() != null) {
        assert getCurrentInstruction() instanceof SSAInvokeInstruction;
      }
      if (getCurrentInstruction() == null) {
        return symbolTable.newSymbol();
      } else {
        SSAInvokeInstruction s = (SSAInvokeInstruction) getCurrentInstruction();
        return s.getException();
      }
    }

    /**
     * Update the machine state to account for an instruction
     */
    class NodeVisitor extends BasicStackMachineVisitor {

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLength(ArrayLengthInstruction)
       */
      @Override
      public void visitArrayLength(com.ibm.wala.shrikeBT.ArrayLengthInstruction instruction) {

        int arrayRef = workingState.pop();
        int length = reuseOrCreateDef();

        workingState.push(length);
        emitInstruction(insts.ArrayLengthInstruction(getCurrentInstructionIndex(), length, arrayRef));

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLoad(IArrayLoadInstruction)
       */
      @Override
      public void visitArrayLoad(IArrayLoadInstruction instruction) {
        int index = workingState.pop();
        int arrayRef = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        if (instruction.isAddressOf()) {
          emitInstruction(insts.AddressOfInstruction(getCurrentInstructionIndex(), result, arrayRef, index, t));
        } else {
           emitInstruction(insts.ArrayLoadInstruction(getCurrentInstructionIndex(), result, arrayRef, index, t));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayStore(IArrayStoreInstruction)
       */
      @Override
      public void visitArrayStore(IArrayStoreInstruction instruction) {

        int value = workingState.pop();
        int index = workingState.pop();
        int arrayRef = workingState.pop();
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.ArrayStoreInstruction(getCurrentInstructionIndex(), arrayRef, index, value, t));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitBinaryOp(IBinaryOpInstruction)
       */
      @Override
      public void visitBinaryOp(IBinaryOpInstruction instruction) {
        int val2 = workingState.pop();
        int val1 = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        boolean isFloat = instruction.getType().equals(TYPE_double) || instruction.getType().equals(TYPE_float);
        emitInstruction(insts.BinaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), instruction.throwsExceptionOnOverflow(), instruction
            .isUnsigned(), result, val1, val2, !isFloat));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitCheckCast(ITypeTestInstruction)
       */
      @Override
      public void visitCheckCast(ITypeTestInstruction instruction) {
        int val = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        if (! instruction.firstClassTypes()) {
          String[] typeNames = instruction.getTypes();
          TypeReference[] t = new TypeReference[ typeNames.length ];
          for(int i = 0; i < typeNames.length; i++) {
            t[i] = ShrikeUtil.makeTypeReference(loader, typeNames[i]);
          }
          emitInstruction(insts.CheckCastInstruction(getCurrentInstructionIndex(), result, val, t, instruction.isPEI()));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitComparison(IComparisonInstruction)
       */
      @Override
      public void visitComparison(IComparisonInstruction instruction) {

        int val2 = workingState.pop();
        int val1 = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        emitInstruction(insts.ComparisonInstruction(getCurrentInstructionIndex(), instruction.getOperator(), result, val1, val2));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConditionalBranch(IConditionalBranchInstruction)
       */
      @Override
      public void visitConditionalBranch(IConditionalBranchInstruction instruction) {
        int val2 = workingState.pop();
        int val1 = workingState.pop();

        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.ConditionalBranchInstruction(getCurrentInstructionIndex(), instruction.getOperator(), t, val1, val2));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConstant(ConstantInstruction)
       */
      @Override
      public void visitConstant(com.ibm.wala.shrikeBT.ConstantInstruction instruction) {
        Language l = cfg.getMethod().getDeclaringClass().getClassLoader().getLanguage();
        TypeReference type = l.getConstantType(instruction.getValue());
        int symbol = 0;
        if (l.isNullType(type)) {
          symbol = symbolTable.getNullConstant();
        } else if (l.isIntType(type)) {
          Integer value = (Integer) instruction.getValue();
          symbol = symbolTable.getConstant(value.intValue());
        } else if (l.isLongType(type)) {
          Long value = (Long) instruction.getValue();
          symbol = symbolTable.getConstant(value.longValue());
        } else if (l.isFloatType(type)) {
          Float value = (Float) instruction.getValue();
          symbol = symbolTable.getConstant(value.floatValue());
        } else if (l.isDoubleType(type)) {
          Double value = (Double) instruction.getValue();
          symbol = symbolTable.getConstant(value.doubleValue());
        } else if (l.isStringType(type)) {
          String value = (String) instruction.getValue();
          symbol = symbolTable.getConstant(value);
        } else if (l.isMetadataType(type)) {
          Object rval = l.getMetadataToken(instruction.getValue());
          symbol = reuseOrCreateDef();
          emitInstruction(insts.LoadMetadataInstruction(getCurrentInstructionIndex(), symbol, type, rval));
        } else {
          Assertions.UNREACHABLE("unexpected " + type);
        }
        workingState.push(symbol);
      }

       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConversion(IConversionInstruction)
       */
      @Override
      public void visitConversion(IConversionInstruction instruction) {

        int val = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);

        TypeReference fromType = ShrikeUtil.makeTypeReference(loader, instruction.getFromType());
        TypeReference toType = ShrikeUtil.makeTypeReference(loader, instruction.getToType());

        emitInstruction(insts.ConversionInstruction(getCurrentInstructionIndex(), result, val, fromType, toType, instruction.throwsExceptionOnOverflow()));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGet(IGetInstruction)
       */
      @Override
      public void visitGet(IGetInstruction instruction) {
        int result = reuseOrCreateDef();
        FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(),
            instruction.getFieldType());
        if (instruction.isAddressOf()) {
          int ref = instruction.isStatic()? -1: workingState.pop();
          emitInstruction(insts.AddressOfInstruction(getCurrentInstructionIndex(), result, ref, f, f.getFieldType()));
        } else if (instruction.isStatic()) {
          emitInstruction(insts.GetInstruction(getCurrentInstructionIndex(), result, f));
        } else {
          int ref = workingState.pop();
          emitInstruction(insts.GetInstruction(getCurrentInstructionIndex(), result, ref, f));
        }
        workingState.push(result);
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGoto(GotoInstruction)
       */
      @Override
      public void visitGoto(com.ibm.wala.shrikeBT.GotoInstruction instruction) {
        emitInstruction(insts.GotoInstruction(getCurrentInstructionIndex()));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitInstanceof(IInstanceofInstruction)
       */
      @Override
      public void visitInstanceof(IInstanceofInstruction instruction) {

        int ref = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.InstanceofInstruction(getCurrentInstructionIndex(), result, ref, t));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitInvoke(IInvokeInstruction)
       */
      @Override
      public void visitInvoke(IInvokeInstruction instruction) {
        doIndirectReads(bytecodeIndirections.indirectlyReadLocals(getCurrentInstructionIndex()));
        int n = instruction.getPoppedCount();
        int[] params = new int[n];
        for (int i = n - 1; i >= 0; i--) {
          params[i] = workingState.pop();
        }
        Language lang = shrikeCFG.getMethod().getDeclaringClass().getClassLoader().getLanguage();
        MethodReference m = MethodReference.findOrCreate(lang, loader, instruction.getClassType(), instruction.getMethodName(),
            instruction.getMethodSignature());
        IInvokeInstruction.IDispatch code = instruction.getInvocationCode();
        CallSiteReference site = CallSiteReference.make(getCurrentProgramCounter(), m, code);
        int exc = reuseOrCreateException();
        if (instruction.getPushedWordSize() > 0) {
          int result = reuseOrCreateDef();
          workingState.push(result);
          emitInstruction(insts.InvokeInstruction(getCurrentInstructionIndex(), result, params, exc, site));
        } else {
          emitInstruction(insts.InvokeInstruction(getCurrentInstructionIndex(), params, exc, site));
        }
        doIndirectWrites(bytecodeIndirections.indirectlyWrittenLocals(getCurrentInstructionIndex()), -1);
      }

      @Override
      public void visitLocalLoad(ILoadInstruction instruction) {
        if (instruction.isAddressOf()) {
          int result = reuseOrCreateDef();
 
          int t = workingState.getLocal(instruction.getVarIndex()); 
          if (t == -1) {
            doIndirectWrites(new int[]{instruction.getVarIndex()}, -1);
            t = workingState.getLocal(instruction.getVarIndex());
          }
          
          TypeReference type = ShrikeUtil.makeTypeReference(loader, instruction.getType());
          emitInstruction(insts.AddressOfInstruction(getCurrentInstructionIndex(), result, t, type));
          workingState.push(result);
        } else {
          super.visitLocalLoad(instruction);
        }
      }

      /*
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitLocalStore(com.ibm.wala.shrikeBT.StoreInstruction)
       */
      @Override
      public void visitLocalStore(IStoreInstruction instruction) {
        if (localMap != null) {
          localMap.startRange(getCurrentInstructionIndex(), instruction.getVarIndex(), workingState.peek());
        }
        super.visitLocalStore(instruction);
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitMonitor(MonitorInstruction)
       */
      @Override
      public void visitMonitor(com.ibm.wala.shrikeBT.MonitorInstruction instruction) {

        int ref = workingState.pop();
        emitInstruction(insts.MonitorInstruction(getCurrentInstructionIndex(), ref, instruction.isEnter()));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitNew(NewInstruction)
       */
      @Override
      public void visitNew(com.ibm.wala.shrikeBT.NewInstruction instruction) {
        int result = reuseOrCreateDef();
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        NewSiteReference ref = NewSiteReference.make(getCurrentProgramCounter(), t);
        if (t.isArrayType()) {
          int[] sizes = new int[instruction.getArrayBoundsCount()];
          for (int i = 0; i < instruction.getArrayBoundsCount(); i++) {
            sizes[instruction.getArrayBoundsCount() - 1 - i] = workingState.pop();
          }
          emitInstruction(insts.NewInstruction(getCurrentInstructionIndex(), result, ref, sizes));
        } else {
          emitInstruction(insts.NewInstruction(getCurrentInstructionIndex(), result, ref));
          popN(instruction);
        }
        workingState.push(result);
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGet(IGetInstruction)
       */
      @Override
      public void visitPut(IPutInstruction instruction) {
        int value = workingState.pop();
        if (instruction.isStatic()) {
          FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(),
              instruction.getFieldType());
          emitInstruction(insts.PutInstruction(getCurrentInstructionIndex(), value, f));
        } else {
          int ref = workingState.pop();
          FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(),
              instruction.getFieldType());
          emitInstruction(insts.PutInstruction(getCurrentInstructionIndex(), ref, value, f));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitReturn(ReturnInstruction)
       */
      @Override
      public void visitReturn(com.ibm.wala.shrikeBT.ReturnInstruction instruction) {
        if (instruction.getPoppedCount() == 1) {
          int result = workingState.pop();
          TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
          emitInstruction(insts.ReturnInstruction(getCurrentInstructionIndex(), result, t.isPrimitiveType()));
        } else {
          emitInstruction(insts.ReturnInstruction(getCurrentInstructionIndex()));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitShift(IShiftInstruction)
       */
      @Override
      public void visitShift(IShiftInstruction instruction) {
        int val2 = workingState.pop();
        int val1 = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        emitInstruction(insts.BinaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), false, instruction.isUnsigned(), result, val1, val2,
            true));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitSwitch(SwitchInstruction)
       */
      @Override
      public void visitSwitch(com.ibm.wala.shrikeBT.SwitchInstruction instruction) {
        int val = workingState.pop();
        emitInstruction(insts.SwitchInstruction(getCurrentInstructionIndex(), val, instruction.getDefaultLabel(), instruction.getCasesAndLabels()));
      }

      private Dominators dom = null;

      private int findRethrowException() {
        int index = getCurrentInstructionIndex();
        SSACFG.BasicBlock bb = cfg.getBlockForInstruction(index);
        if (bb.isCatchBlock()) {
          SSACFG.ExceptionHandlerBasicBlock newBB = (SSACFG.ExceptionHandlerBasicBlock) bb;
          SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction();
          return s.getDef();
        } else {
          // TODO: should we really use dominators here? maybe it would be cleaner to propagate
          // the notion of 'current exception to rethrow' using the abstract interpreter.
          if (dom == null) {
            dom = Dominators.make(cfg, cfg.entry());
          }

          ISSABasicBlock x = bb;
          while (x != null) {
            if (x.isCatchBlock()) {
              SSACFG.ExceptionHandlerBasicBlock newBB = (SSACFG.ExceptionHandlerBasicBlock) x;
              SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction();
              return s.getDef();
            } else {
              x = dom.getIdom(x);
            }
          }

          // assert false;
          return -1;
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitThrow(ThrowInstruction)
       */
      @Override
      public void visitThrow(com.ibm.wala.shrikeBT.ThrowInstruction instruction) {
        if (instruction.isRethrow()) {
          workingState.clearStack();
          emitInstruction(insts.ThrowInstruction(getCurrentInstructionIndex(), findRethrowException()));
        } else {
          int exception = workingState.pop();
          workingState.clearStack();
          workingState.push(exception);
          emitInstruction(insts.ThrowInstruction(getCurrentInstructionIndex(), exception));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitUnaryOp(IUnaryOpInstruction)
       */
      @Override
      public void visitUnaryOp(IUnaryOpInstruction instruction) {
        int val = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        emitInstruction(insts.UnaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), result, val));
      }

      private void doIndirectReads(int[] locals) {
        for(int i = 0; i < locals.length; i++) {
          ssaIndirections.setUse(getCurrentInstructionIndex(), new ShrikeLocalName(locals[i]), workingState.getLocal(locals[i]));
        }
      }
      
      @Override
      public void visitLoadIndirect(ILoadIndirectInstruction instruction) { 
        int addressVal = workingState.pop();
        int result = reuseOrCreateDef();
        doIndirectReads(bytecodeIndirections.indirectlyReadLocals(getCurrentInstructionIndex()));
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getPushedType(null));
        emitInstruction(insts.LoadIndirectInstruction(getCurrentInstructionIndex(), result, t, addressVal));
        workingState.push(result);
      }

      private void doIndirectWrites(int[] locals, int rval) {
        for(int i = 0; i < locals.length; i++) {
          ShrikeLocalName name = new ShrikeLocalName(locals[i]);
          int idx = getCurrentInstructionIndex();
          if (ssaIndirections.getDef(idx, name) == -1) {
            ssaIndirections.setDef(idx, name, rval==-1? symbolTable.newSymbol(): rval);
          }
          workingState.setLocal(locals[i], ssaIndirections.getDef(idx, name));
        }        
      }
      
      @Override
      public void visitStoreIndirect(IStoreIndirectInstruction instruction) {  
        int val = workingState.pop();        
        int addressVal = workingState.pop();
        doIndirectWrites(bytecodeIndirections.indirectlyWrittenLocals(getCurrentInstructionIndex()), val);     
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.StoreIndirectInstruction(getCurrentInstructionIndex(), addressVal, val, t));
      }

    }

    /**
     * @param piCause
     * @param ref
     */
    private void reuseOrCreatePi(SSAInstruction piCause, int ref) {
      int n = getCurrentInstructionIndex();
      SSACFG.BasicBlock bb = cfg.getBlockForInstruction(n);

      BasicBlock path = getCurrentSuccessor();
      int outNum = shrikeCFG.getNumber(path);

      SSAPiInstruction pi = bb.getPiForRefAndPath(ref, path);
      if (pi == null) {
        pi = insts.PiInstruction(SSAInstruction.NO_INDEX, symbolTable.newSymbol(), ref, bb.getNumber(), outNum, piCause);
        bb.addPiForRefAndPath(ref, path, pi);
      }

      workingState.replaceValue(ref, pi.getDef());
    }

    // private void maybeInsertPi(int val) {
    // if ((addPiForFieldSelect) && (creators.length > val) && (creators[val] instanceof SSAGetInstruction)
    // && !((SSAGetInstruction) creators[val]).isStatic()) {
    // reuseOrCreatePi(creators[val], val);
    // } else if ((addPiForDispatchSelect)
    // && (creators.length > val)
    // && (creators[val] instanceof SSAInvokeInstruction)
    // && (((SSAInvokeInstruction) creators[val]).getInvocationCode() == IInvokeInstruction.Dispatch.VIRTUAL ||
    // ((SSAInvokeInstruction) creators[val])
    // .getInvocationCode() == IInvokeInstruction.Dispatch.INTERFACE)) {
    // reuseOrCreatePi(creators[val], val);
    // }
    // }

    private void maybeInsertPi(SSAAbstractInvokeInstruction call) {
      if (piNodePolicy != null) {
        Pair pi = piNodePolicy.getPi(call, symbolTable);
        if (pi != null) {
          reuseOrCreatePi(pi.snd, pi.fst);
        }
      }
    }

    private void maybeInsertPi(SSAConditionalBranchInstruction cond) {
      if (piNodePolicy != null) {
        Pair pi = piNodePolicy.getPi(cond, getDef(cond.getUse(0)), getDef(cond.getUse(1)), symbolTable);
        if (pi != null) {
          reuseOrCreatePi(pi.snd, pi.fst);
        }
      }
    }

    private SSAInstruction getDef(int vn) {
      if (vn < creators.length) {
        return creators[vn];
      } else {
        return null;
      }
    }

    class EdgeVisitor extends com.ibm.wala.shrikeBT.IInstruction.Visitor {

      @Override
      public void visitInvoke(IInvokeInstruction instruction) {
        maybeInsertPi((SSAAbstractInvokeInstruction) getCurrentInstruction());
      }

      @Override
      public void visitConditionalBranch(IConditionalBranchInstruction instruction) {
        maybeInsertPi((SSAConditionalBranchInstruction) getCurrentInstruction());
      }
    }

    @Override
    public com.ibm.wala.shrikeBT.IInstruction[] getInstructions() {
      try {
        return shrikeCFG.getMethod().getInstructions();
      } catch (InvalidClassFileException e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
        return null;
      }
    }
  }

  /**
   * Build the IR
   */
  public void build() {
    solve();
    if (localMap != null) {
      localMap.finishLocalMap(this);
    }
  }

  public SSA2LocalMap getLocalMap() {
    return localMap;
  }

  public ShrikeIndirectionData getIndirectionData() {
    return ssaIndirections;
  }
  
  /**
   * A logical mapping from  -> local number Note: make sure this class remains static: this persists as part of
   * the IR!!
   */
  private static class SSA2LocalMap implements com.ibm.wala.ssa.IR.SSA2LocalMap {

    private final ShrikeCFG shrikeCFG;

    /**
     * Mapping Integer -> IntPair where p maps to (vn,L) iff we've started a range at pc p where value number vn corresponds to
     * local L
     */
    private final IntPair[] localStoreMap;

    /**
     * For each basic block i and local j, block2LocalState[i][j] gives the contents of local j at the start of block i
     */
    private final int[][] block2LocalState;

    /**
     * @param nInstructions number of instructions in the bytecode for this method
     * @param nBlocks number of basic blocks in the CFG
     */
    SSA2LocalMap(ShrikeCFG shrikeCfg, int nInstructions, int nBlocks) {
      shrikeCFG = shrikeCfg;
      localStoreMap = new IntPair[nInstructions];
      block2LocalState = new int[nBlocks][];
    }

    /**
      for (int i = 0; i < x.length; i++) {
      }
     * Record the beginning of a new range, starting at the given program counter, in which a particular value number corresponds to
     * a particular local number
     */
    void startRange(int pc, int localNumber, int valueNumber) {
      localStoreMap[pc] = new IntPair(valueNumber, localNumber);
    }

    /**
     * Finish populating the map of local variable information
     */
    private void finishLocalMap(SSABuilder builder) {
      for (Iterator it = shrikeCFG.iterator(); it.hasNext();) {
        ShrikeCFG.BasicBlock bb = (ShrikeCFG.BasicBlock) it.next();
        MachineState S = builder.getIn(bb);
        int number = bb.getNumber();
        block2LocalState[number] = S.getLocals();
      }
    }

    /**
     * @param index - index into IR instruction array
     * @param vn - value number
     */
    public String[] getLocalNames(int index, int vn) {
      try {
        if (!shrikeCFG.getMethod().hasLocalVariableTable()) {
          return null;
        } else {
          int[] localNumbers = findLocalsForValueNumber(index, vn);
          if (localNumbers == null) {
            return null;
          } else {
            IBytecodeMethod m = shrikeCFG.getMethod();
            String[] result = new String[localNumbers.length];
            for (int i = 0; i < localNumbers.length; i++) {
              result[i] = m.getLocalVariableName(m.getBytecodeIndex(index), localNumbers[i]);
            }
            return result;
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
        return null;
      }
    }

    public int[] allocateNewLocalsArray(int maxLocals) {
      int[] result = new int[maxLocals];
      for (int i = 0; i < maxLocals; i++) {
        result[i] = OPTIMISTIC ? TOP : BOTTOM;
      }
      return result;
    }
    
    private int[] setLocal(int[] locals, int localNumber, int valueNumber) {
      if (locals == null) {
        locals = allocateNewLocalsArray(localNumber + 1);
      } else if (locals.length <= localNumber) {
        int[] newLocals = allocateNewLocalsArray(2 * Math.max(locals.length, localNumber) + 1);
        System.arraycopy(locals, 0, newLocals, 0, locals.length);
        locals = newLocals;
      }
      
      locals[localNumber] = valueNumber;
      
      return locals;
    }
    /**
     * @param pc a program counter (index into ShrikeBT instruction array)
     * @param vn a value number
     * @return if we know that immediately after the given program counter, v_vn corresponds to some set of locals, then return an
     *         array of the local numbers. else return null.
     */
    private int[] findLocalsForValueNumber(int pc, int vn) {
      if (vn < 0) {
        return null;
      }
      IBasicBlock bb = shrikeCFG.getBlockForInstruction(pc);
      int firstInstruction = bb.getFirstInstructionIndex();
      // walk forward from the first instruction to reconstruct the
      // state of the locals at this pc
      int[] locals = block2LocalState[bb.getNumber()];
      for (int i = firstInstruction; i <= pc; i++) {
        if (localStoreMap[i] != null) {
          IntPair p = localStoreMap[i];
          locals = setLocal(locals, p.getY(), p.getX());
        }
      }
      return locals == null ? null : extractIndices(locals, vn);
    }

    /**
     * @return the indices i s.t. x[i] == y, or null if none found.
     */
    private int[] extractIndices(int[] x, int y) {
      assert x != null;
      int count = 0;

        if (x[i] == y) {
          count++;
        }
      }
      if (count == 0) {
        return null;
      } else {
        int[] result = new int[count];
        int j = 0;
        for (int i = 0; i < x.length; i++) {
          if (x[i] == y) {
            result[j++] = i;
          }
        }
        return result;
      }
    }
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import java.util.Iterator;

import com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.cfg.ShrikeCFG;
import com.ibm.wala.cfg.ShrikeCFG.BasicBlock;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IBytecodeMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.shrikeBT.ArrayLengthInstruction;
import com.ibm.wala.shrikeBT.ConstantInstruction;
import com.ibm.wala.shrikeBT.GotoInstruction;
import com.ibm.wala.shrikeBT.IArrayLoadInstruction;
import com.ibm.wala.shrikeBT.IArrayStoreInstruction;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IComparisonInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IConversionInstruction;
import com.ibm.wala.shrikeBT.IGetInstruction;
import com.ibm.wala.shrikeBT.IInstanceofInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeBT.ILoadIndirectInstruction;
import com.ibm.wala.shrikeBT.ILoadInstruction;
import com.ibm.wala.shrikeBT.IPutInstruction;
import com.ibm.wala.shrikeBT.IShiftInstruction;
import com.ibm.wala.shrikeBT.IStoreIndirectInstruction;
import com.ibm.wala.shrikeBT.IStoreInstruction;
import com.ibm.wala.shrikeBT.ITypeTestInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.IndirectionData;
import com.ibm.wala.shrikeBT.MonitorInstruction;
import com.ibm.wala.shrikeBT.NewInstruction;
import com.ibm.wala.shrikeBT.ReturnInstruction;
import com.ibm.wala.shrikeBT.SwitchInstruction;
import com.ibm.wala.shrikeBT.ThrowInstruction;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.ShrikeIndirectionData.ShrikeLocalName;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.dominators.Dominators;
import com.ibm.wala.util.intset.IntPair;
import com.ibm.wala.util.shrike.ShrikeUtil;

/**
 * This class constructs an SSA {@link IR} from a backing ShrikeBT instruction stream.
 * 
 * The basic algorithm here is an abstract interpretation over the Java bytecode to determine types of stack locations and local
 * variables. As a side effect, the flow functions of the abstract interpretation emit instructions, eliminating the stack
 * abstraction and moving to a register-transfer language in SSA form.
 */
public class SSABuilder extends AbstractIntStackMachine {

  public static SSABuilder make(IBytecodeMethod method, SSACFG cfg, ShrikeCFG scfg, SSAInstruction[] instructions,
    // it reaches a block, and thus no piece of the CFG will be skipped.
      SymbolTable symbolTable, boolean buildLocalMap, SSAPiNodePolicy piNodePolicy) throws IllegalArgumentException {
    if (scfg == null) {
      throw new IllegalArgumentException("scfg == null");
    }
    return new SSABuilder(method, cfg, scfg, instructions, symbolTable, buildLocalMap, piNodePolicy);
  }

  /**
   * A wrapper around the method being analyzed.
   */
  final private IBytecodeMethod method;

  /**
   * Governing symbol table
   */
  final private SymbolTable symbolTable;

  /**
   * A logical mapping from  -> local number if null, don't build it.
   */
  private final SSA2LocalMap localMap;

  /**
   * a factory to create concrete instructions
   */
  private final SSAInstructionFactory insts;

  /**
   * information about indirect use of local variables in the bytecode
   */
  private final IndirectionData bytecodeIndirections;
  
  private final ShrikeIndirectionData ssaIndirections;
  
  private SSABuilder(IBytecodeMethod method, SSACFG cfg, ShrikeCFG scfg, SSAInstruction[] instructions, SymbolTable symbolTable,
      boolean buildLocalMap, SSAPiNodePolicy piNodePolicy) {
    super(scfg);
    localMap = buildLocalMap ? new SSA2LocalMap(scfg, instructions.length, cfg.getNumberOfNodes()) : null;
    init(new SymbolTableMeeter(symbolTable, cfg, scfg), new SymbolicPropagator(scfg, instructions, symbolTable,
        localMap, cfg, piNodePolicy));
      
    this.method = method;
    this.symbolTable = symbolTable;
    this.insts = method.getDeclaringClass().getClassLoader().getInstructionFactory();
    this.bytecodeIndirections = method.getIndirectionData();
    this.ssaIndirections = new ShrikeIndirectionData(instructions.length);
    assert cfg != null : "Null CFG";
  }

  private class SymbolTableMeeter implements Meeter {

    final SSACFG cfg;


    final SymbolTable symbolTable;

    final ShrikeCFG shrikeCFG;

    SymbolTableMeeter(SymbolTable symbolTable, SSACFG cfg, ShrikeCFG shrikeCFG) {
      this.cfg = cfg;
      this.symbolTable = symbolTable;
      this.shrikeCFG = shrikeCFG;
    }

    public int meetStack(int slot, int[] rhs, BasicBlock bb) {

      assert bb != null : "null basic block";

      if (bb.isExitBlock()) {
        return TOP;
      }

      if (allTheSame(rhs)) {
        for (int i = 0; i < rhs.length; i++) {
          if (rhs[i] != TOP) {
            return rhs[i];
          }
        }
        // didn't find anything but TOP
        return TOP;
      } else {
        SSACFG.BasicBlock newBB = cfg.getNode(shrikeCFG.getNumber(bb));
        // if we already have a phi for this stack location
        SSAPhiInstruction phi = newBB.getPhiForStackSlot(slot);
        int result;
        if (phi == null) {
          // no phi already exists. create one.
          result = symbolTable.newPhi(rhs);
          PhiValue v = symbolTable.getPhiValue(result);
          phi = v.getPhiInstruction();
          newBB.addPhiForStackSlot(slot, phi);
        } else {
          // already created a phi. update it to account for the
          // new merge.
          result = phi.getDef();
          phi.setValues(rhs.clone());
        }
        return result;
      }
    }

    /**
    //
      /**
     * @see com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine.Meeter#meetLocal(int, int[], BasicBlock)
     */
    public int meetLocal(int n, int[] rhs, BasicBlock bb) {
      if (allTheSame(rhs)) {
        for (int i = 0; i < rhs.length; i++) {
          if (rhs[i] != TOP) {
            return rhs[i];
          }
        }
        // didn't find anything but TOP
        return TOP;
      } else {
        SSACFG.BasicBlock newBB = cfg.getNode(shrikeCFG.getNumber(bb));
        if (bb.isExitBlock()) {
          // no phis in exit block please
          return TOP;
        }
        // if we already have a phi for this local
        SSAPhiInstruction phi = newBB.getPhiForLocal(n);
        int result;
        if (phi == null) {
          // no phi already exists. create one.
          result = symbolTable.newPhi(rhs);
          PhiValue v = symbolTable.getPhiValue(result);
          phi = v.getPhiInstruction();
          newBB.addPhiForLocal(n, phi);
        } else {
          // already created a phi. update it to account for the
          // new merge.
          result = phi.getDef();
          phi.setValues(rhs.clone());
        }
        return result;
      }
    }

    /**
     * Are all rhs values all the same? Note, we consider TOP (-1) to be same as everything else.
     * 
     * @param rhs
     * @return boolean
     */
    private boolean allTheSame(int[] rhs) {
      int x = -1;
      // set x := the first non-TOP value
      int i = 0;
      for (i = 0; i < rhs.length; i++) {
        if (rhs[i] != TOP) {
          x = rhs[i];
          break;
        }
      }
      // check the remaining values
      for (i = i + 1; i < rhs.length; i++) {
        if (rhs[i] != x && rhs[i] != TOP)
          return false;
      }
      return true;
    }

    /**
     * @see com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine.Meeter#meetStackAtCatchBlock(BasicBlock)
     */
    public int meetStackAtCatchBlock(BasicBlock bb) {
      int bbNumber = shrikeCFG.getNumber(bb);
      SSACFG.ExceptionHandlerBasicBlock newBB = (SSACFG.ExceptionHandlerBasicBlock) cfg.getNode(bbNumber);
      SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction();
      int exceptionValue;
      if (s == null) {
        exceptionValue = symbolTable.newSymbol();
        s = insts.GetCaughtExceptionInstruction(bbNumber, exceptionValue);
        newBB.setCatchInstruction(s);
      } else {
        exceptionValue = s.getException();
      }
      return exceptionValue;

    }
  }

  @Override
  protected void initializeVariables() {
    MachineState entryState = getEntryState();
    int parameterNumber = 0;
    int local = -1;
    for (int i = 0; i < method.getNumberOfParameters(); i++) {
      local++;
      TypeReference t = method.getParameterType(i);
      if (t != null) {
        int symbol = symbolTable.getParameter(parameterNumber++);
        entryState.setLocal(local, symbol);
        if (t.equals(TypeReference.Double) || t.equals(TypeReference.Long)) {
          local++;
        }
      }
    }

    // This useless value ensures that the state cannot be empty, even
    // for a static method with no arguments in blocks with an empty stack
    // and no locals being used. This ensures that propagation of the
    // state thru the CFGSystem will always show changes the first time
    /**
    // (note that this bizarre state really happened, in java_cup)
    //
    // SJF: I don't understand how this is supposed to work. It
    // causes a bug right now in normal cases, so I'm commenting it out
    // for now. If there's a problem, let's add a regression test
    // to catch it.
    //
    entryState.push(symbolTable.newSymbol());
  }

  /**
   * This class defines the type abstractions for this analysis and the flow function for each instruction in the ShrikeBT IR.
   */
  private class SymbolicPropagator extends BasicStackFlowProvider {

    final SSAInstruction[] instructions;

    final SymbolTable symbolTable;

    final ShrikeCFG shrikeCFG;

    final SSACFG cfg;

    final ClassLoaderReference loader;

    /**
     * creators[i] holds the instruction that defs value number i
     */
    private SSAInstruction[] creators;

    final SSA2LocalMap localMap;

    final SSAPiNodePolicy piNodePolicy;

    public SymbolicPropagator(ShrikeCFG shrikeCFG, SSAInstruction[] instructions, SymbolTable symbolTable, SSA2LocalMap localMap,
        SSACFG cfg, SSAPiNodePolicy piNodePolicy) {
      super(shrikeCFG);
      this.piNodePolicy = piNodePolicy;
      this.cfg = cfg;
      this.creators = new SSAInstruction[0];
      this.shrikeCFG = shrikeCFG;
      this.instructions = instructions;
      this.symbolTable = symbolTable;
      this.loader = shrikeCFG.getMethod().getDeclaringClass().getClassLoader().getReference();
      this.localMap = localMap;
      init(this.new NodeVisitor(), this.new EdgeVisitor());
    }

    @Override
    public boolean needsEdgeFlow() {
      return piNodePolicy != null;
    }

    private void emitInstruction(SSAInstruction s) {
      if (s != null) {
        instructions[getCurrentInstructionIndex()] = s;
        for (int i = 0; i < s.getNumberOfDefs(); i++) {
          if (creators.length < (s.getDef(i) + 1)) {
            SSAInstruction[] arr = new SSAInstruction[2 * s.getDef(i)];
            System.arraycopy(creators, 0, arr, 0, creators.length);
            creators = arr;
          }

          assert s.getDef(i) != -1 : "invalid def " + i + " for " + s;
        
          creators[s.getDef(i)] = s;
        }
      }
    }

    private SSAInstruction getCurrentInstruction() {
      return instructions[getCurrentInstructionIndex()];
    }

    /**
     * If we've already created the current instruction, return the value number def'ed by the current instruction. Else, create a
     * new symbol.
     */
    private int reuseOrCreateDef() {
      if (getCurrentInstruction() == null) {
        return symbolTable.newSymbol();
      } else {
        return getCurrentInstruction().getDef();
      }
    }

    /**
     * If we've already created the current instruction, return the value number representing the exception the instruction may
     * throw. Else, create a new symbol
     */
    private int reuseOrCreateException() {

      if (getCurrentInstruction() != null) {
        assert getCurrentInstruction() instanceof SSAInvokeInstruction;
      }
      if (getCurrentInstruction() == null) {
        return symbolTable.newSymbol();
      } else {
        SSAInvokeInstruction s = (SSAInvokeInstruction) getCurrentInstruction();
        return s.getException();
      }
    }

     * Update the machine state to account for an instruction
     */
    class NodeVisitor extends BasicStackMachineVisitor {

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLength(ArrayLengthInstruction)
       */
      @Override
      public void visitArrayLength(com.ibm.wala.shrikeBT.ArrayLengthInstruction instruction) {

        int arrayRef = workingState.pop();
        int length = reuseOrCreateDef();

        workingState.push(length);
        emitInstruction(insts.ArrayLengthInstruction(length, arrayRef));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLoad(IArrayLoadInstruction)
       */
      @Override
      public void visitArrayLoad(IArrayLoadInstruction instruction) {
        int index = workingState.pop();
        int arrayRef = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        if (instruction.isAddressOf()) {
          emitInstruction(insts.AddressOfInstruction(result, arrayRef, index, t));
        } else {
           emitInstruction(insts.ArrayLoadInstruction(result, arrayRef, index, t));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayStore(IArrayStoreInstruction)
       */
      @Override
      @Override
      public void visitArrayStore(IArrayStoreInstruction instruction) {

        int value = workingState.pop();
        int index = workingState.pop();
        int arrayRef = workingState.pop();
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.ArrayStoreInstruction(arrayRef, index, value, t));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitBinaryOp(IBinaryOpInstruction)
       */
      @Override
      public void visitBinaryOp(IBinaryOpInstruction instruction) {
        int val2 = workingState.pop();
        int val1 = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        boolean isFloat = instruction.getType().equals(TYPE_double) || instruction.getType().equals(TYPE_float);
        emitInstruction(insts.BinaryOpInstruction(instruction.getOperator(), instruction.throwsExceptionOnOverflow(), instruction
            .isUnsigned(), result, val1, val2, !isFloat));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitCheckCast(ITypeTestInstruction)
       */
      @Override
      public void visitCheckCast(ITypeTestInstruction instruction) {
        int val = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        if (! instruction.firstClassTypes()) {
          String[] typeNames = instruction.getTypes();
          TypeReference[] t = new TypeReference[ typeNames.length ];
          for(int i = 0; i < typeNames.length; i++) {
            t[i] = ShrikeUtil.makeTypeReference(loader, typeNames[i]);
          }
          emitInstruction(insts.CheckCastInstruction(result, val, t, instruction.isPEI()));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitComparison(IComparisonInstruction)
       */
      @Override
      public void visitComparison(IComparisonInstruction instruction) {

        int val2 = workingState.pop();
        int val1 = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        emitInstruction(insts.ComparisonInstruction(instruction.getOperator(), result, val1, val2));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConditionalBranch(IConditionalBranchInstruction)
       */
      @Override
      public void visitConditionalBranch(IConditionalBranchInstruction instruction) {
        int val2 = workingState.pop();
        int val1 = workingState.pop();

        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.ConditionalBranchInstruction(instruction.getOperator(), t, val1, val2));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConstant(ConstantInstruction)
       */
      @Override
      public void visitConstant(com.ibm.wala.shrikeBT.ConstantInstruction instruction) {
        Language l = cfg.getMethod().getDeclaringClass().getClassLoader().getLanguage();
        TypeReference type = l.getConstantType(instruction.getValue());
        int symbol = 0;
        if (l.isNullType(type)) {
          symbol = symbolTable.getNullConstant();
        } else if (l.isIntType(type)) {
          Integer value = (Integer) instruction.getValue();
          symbol = symbolTable.getConstant(value.intValue());
        } else if (l.isLongType(type)) {
          Long value = (Long) instruction.getValue();
          symbol = symbolTable.getConstant(value.longValue());
        } else if (l.isFloatType(type)) {
          Float value = (Float) instruction.getValue();
          symbol = symbolTable.getConstant(value.floatValue());
        } else if (l.isDoubleType(type)) {
          Double value = (Double) instruction.getValue();
          symbol = symbolTable.getConstant(value.doubleValue());
        } else if (l.isStringType(type)) {
          String value = (String) instruction.getValue();
          symbol = symbolTable.getConstant(value);
        } else if (l.isMetadataType(type)) {
          Object rval = l.getMetadataToken(instruction.getValue());
          symbol = reuseOrCreateDef();
          emitInstruction(insts.LoadMetadataInstruction(symbol, type, rval));
        } else {
          Assertions.UNREACHABLE("unexpected " + type);
        }
        workingState.push(symbol);
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConversion(IConversionInstruction)
       */
      @Override
      public void visitConversion(IConversionInstruction instruction) {

        int val = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);

        TypeReference fromType = ShrikeUtil.makeTypeReference(loader, instruction.getFromType());
        TypeReference toType = ShrikeUtil.makeTypeReference(loader, instruction.getToType());

        emitInstruction(insts.ConversionInstruction(result, val, fromType, toType, instruction.throwsExceptionOnOverflow()));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGet(IGetInstruction)
       */
      @Override
      public void visitGet(IGetInstruction instruction) {
        int result = reuseOrCreateDef();
        FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(),
            instruction.getFieldType());
        if (instruction.isAddressOf()) {
          int ref = instruction.isStatic()? -1: workingState.pop();
          emitInstruction(insts.AddressOfInstruction(result, ref, f, f.getFieldType()));
        } else if (instruction.isStatic()) {
          emitInstruction(insts.GetInstruction(result, f));
        } else {
          int ref = workingState.pop();
          emitInstruction(insts.GetInstruction(result, ref, f));
        }
        workingState.push(result);
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGoto(GotoInstruction)
       */
      @Override
      public void visitGoto(com.ibm.wala.shrikeBT.GotoInstruction instruction) {
        emitInstruction(insts.GotoInstruction());
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitInstanceof(IInstanceofInstruction)
       */
      @Override
      public void visitInstanceof(IInstanceofInstruction instruction) {

        int ref = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.InstanceofInstruction(result, ref, t));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitInvoke(IInvokeInstruction)
       */
      @Override
      public void visitInvoke(IInvokeInstruction instruction) {
        doIndirectReads(bytecodeIndirections.indirectlyReadLocals(getCurrentInstructionIndex()));
        int n = instruction.getPoppedCount();
        int[] params = new int[n];
        for (int i = n - 1; i >= 0; i--) {
          params[i] = workingState.pop();
        }
        Language lang = shrikeCFG.getMethod().getDeclaringClass().getClassLoader().getLanguage();
        MethodReference m = MethodReference.findOrCreate(lang, loader, instruction.getClassType(), instruction.getMethodName(),
            instruction.getMethodSignature());
        IInvokeInstruction.IDispatch code = instruction.getInvocationCode();
        CallSiteReference site = CallSiteReference.make(getCurrentProgramCounter(), m, code);
        int exc = reuseOrCreateException();
        if (instruction.getPushedWordSize() > 0) {
          int result = reuseOrCreateDef();
          workingState.push(result);
          emitInstruction(insts.InvokeInstruction(result, params, exc, site));
        } else {
          emitInstruction(insts.InvokeInstruction(params, exc, site));
        }
        doIndirectWrites(bytecodeIndirections.indirectlyWrittenLocals(getCurrentInstructionIndex()), -1);
      }

      @Override
      public void visitLocalLoad(ILoadInstruction instruction) {
        if (instruction.isAddressOf()) {
          int result = reuseOrCreateDef();
 
          int t = workingState.getLocal(instruction.getVarIndex()); 
          if (t == -1) {
            doIndirectWrites(new int[]{instruction.getVarIndex()}, -1);
            t = workingState.getLocal(instruction.getVarIndex());
          }
          
          TypeReference type = ShrikeUtil.makeTypeReference(loader, instruction.getType());
          emitInstruction(insts.AddressOfInstruction(result, t, type));
          workingState.push(result);
        } else {
          super.visitLocalLoad(instruction);
        }
      }

      /*
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitLocalStore(com.ibm.wala.shrikeBT.StoreInstruction)
       */
      @Override
      public void visitLocalStore(IStoreInstruction instruction) {
        if (localMap != null) {
          localMap.startRange(getCurrentInstructionIndex(), instruction.getVarIndex(), workingState.peek());
        }
        super.visitLocalStore(instruction);
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitMonitor(MonitorInstruction)
       */
      @Override
      public void visitMonitor(com.ibm.wala.shrikeBT.MonitorInstruction instruction) {

        int ref = workingState.pop();
        emitInstruction(insts.MonitorInstruction(ref, instruction.isEnter()));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitNew(NewInstruction)
       */
      @Override
      public void visitNew(com.ibm.wala.shrikeBT.NewInstruction instruction) {
        int result = reuseOrCreateDef();
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        NewSiteReference ref = NewSiteReference.make(getCurrentProgramCounter(), t);
        if (t.isArrayType()) {
          int[] sizes = new int[instruction.getArrayBoundsCount()];
          for (int i = 0; i < instruction.getArrayBoundsCount(); i++) {
            sizes[instruction.getArrayBoundsCount() - 1 - i] = workingState.pop();
          }
          emitInstruction(insts.NewInstruction(result, ref, sizes));
        } else {
          emitInstruction(insts.NewInstruction(result, ref));
          popN(instruction);
        }
        workingState.push(result);
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGet(IGetInstruction)
       */
      @Override
      public void visitPut(IPutInstruction instruction) {
        int value = workingState.pop();
        if (instruction.isStatic()) {
          FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(),
              instruction.getFieldType());
          emitInstruction(insts.PutInstruction(value, f));
        } else {
          int ref = workingState.pop();
          FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(),
              instruction.getFieldType());
          emitInstruction(insts.PutInstruction(ref, value, f));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitReturn(ReturnInstruction)
       */
      @Override
      public void visitReturn(com.ibm.wala.shrikeBT.ReturnInstruction instruction) {
        if (instruction.getPoppedCount() == 1) {
          int result = workingState.pop();
          TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
          emitInstruction(insts.ReturnInstruction(result, t.isPrimitiveType()));
        } else {
          emitInstruction(insts.ReturnInstruction());
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitShift(IShiftInstruction)
       */
      @Override
      public void visitShift(IShiftInstruction instruction) {
        int val2 = workingState.pop();
        int val1 = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        emitInstruction(insts.BinaryOpInstruction(instruction.getOperator(), false, instruction.isUnsigned(), result, val1, val2,
            true));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitSwitch(SwitchInstruction)
       */
      @Override
      public void visitSwitch(com.ibm.wala.shrikeBT.SwitchInstruction instruction) {
        int val = workingState.pop();
        emitInstruction(insts.SwitchInstruction(val, instruction.getDefaultLabel(), instruction.getCasesAndLabels()));
      }

      private Dominators dom = null;

      private int findRethrowException() {
        int index = getCurrentInstructionIndex();
        SSACFG.BasicBlock bb = cfg.getBlockForInstruction(index);
        if (bb.isCatchBlock()) {
          SSACFG.ExceptionHandlerBasicBlock newBB = (SSACFG.ExceptionHandlerBasicBlock) bb;
          SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction();
          return s.getDef();
        } else {
          // TODO: should we really use dominators here? maybe it would be cleaner to propagate
          // the notion of 'current exception to rethrow' using the abstract interpreter.
          if (dom == null) {
            dom = Dominators.make(cfg, cfg.entry());
          }

          ISSABasicBlock x = bb;
          while (x != null) {
            if (x.isCatchBlock()) {
              SSACFG.ExceptionHandlerBasicBlock newBB = (SSACFG.ExceptionHandlerBasicBlock) x;
              SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction();
              return s.getDef();
            } else {
              x = dom.getIdom(x);
            }
          }

          // assert false;
          return -1;
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitThrow(ThrowInstruction)
       */
      @Override
      public void visitThrow(com.ibm.wala.shrikeBT.ThrowInstruction instruction) {
        if (instruction.isRethrow()) {
          workingState.clearStack();
          emitInstruction(insts.ThrowInstruction(findRethrowException()));
        } else {
          int exception = workingState.pop();
          workingState.clearStack();
          workingState.push(exception);
          emitInstruction(insts.ThrowInstruction(exception));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitUnaryOp(IUnaryOpInstruction)
       */
      @Override
      public void visitUnaryOp(IUnaryOpInstruction instruction) {
        int val = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        emitInstruction(insts.UnaryOpInstruction(instruction.getOperator(), result, val));
      }

      private void doIndirectReads(int[] locals) {
        for(int i = 0; i < locals.length; i++) {
          ssaIndirections.setUse(getCurrentInstructionIndex(), new ShrikeLocalName(locals[i]), workingState.getLocal(locals[i]));
        }
      }
      public void visitLoadIndirect(ILoadIndirectInstruction instruction) { 
        int addressVal = workingState.pop();
        int result = reuseOrCreateDef();
        doIndirectReads(bytecodeIndirections.indirectlyReadLocals(getCurrentInstructionIndex()));
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getPushedType(null));
        emitInstruction(insts.LoadIndirectInstruction(result, t, addressVal));
        workingState.push(result);
      }

      private void doIndirectWrites(int[] locals, int rval) {
        for(int i = 0; i < locals.length; i++) {
          ShrikeLocalName name = new ShrikeLocalName(locals[i]);
          int idx = getCurrentInstructionIndex();
          if (ssaIndirections.getDef(idx, name) == -1) {
            ssaIndirections.setDef(idx, name, rval==-1? symbolTable.newSymbol(): rval);
          }
          workingState.setLocal(locals[i], ssaIndirections.getDef(idx, name));
        }        
      }
      
      @Override
      public void visitStoreIndirect(IStoreIndirectInstruction instruction) {  
        int val = workingState.pop();        
        int addressVal = workingState.pop();
        doIndirectWrites(bytecodeIndirections.indirectlyWrittenLocals(getCurrentInstructionIndex()), val);     
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.StoreIndirectInstruction(addressVal, val, t));
      }

    }

    /**
     * @param piCause
     * @param ref
     */
    private void reuseOrCreatePi(SSAInstruction piCause, int ref) {
      int n = getCurrentInstructionIndex();
      SSACFG.BasicBlock bb = cfg.getBlockForInstruction(n);

      BasicBlock path = getCurrentSuccessor();
      int outNum = shrikeCFG.getNumber(path);

      SSAPiInstruction pi = bb.getPiForRefAndPath(ref, path);
      if (pi == null) {
        pi = insts.PiInstruction(symbolTable.newSymbol(), ref, bb.getNumber(), outNum, piCause);
        bb.addPiForRefAndPath(ref, path, pi);
      }

      workingState.replaceValue(ref, pi.getDef());
    }

    // private void maybeInsertPi(int val) {
    // if ((addPiForFieldSelect) && (creators.length > val) && (creators[val] instanceof SSAGetInstruction)
    // && !((SSAGetInstruction) creators[val]).isStatic()) {
    // reuseOrCreatePi(creators[val], val);
    // } else if ((addPiForDispatchSelect)
    // && (creators.length > val)
    // && (creators[val] instanceof SSAInvokeInstruction)
    // && (((SSAInvokeInstruction) creators[val]).getInvocationCode() == IInvokeInstruction.Dispatch.VIRTUAL ||
    // ((SSAInvokeInstruction) creators[val])
    // .getInvocationCode() == IInvokeInstruction.Dispatch.INTERFACE)) {
    // reuseOrCreatePi(creators[val], val);
    // }
    // }

    private void maybeInsertPi(SSAAbstractInvokeInstruction call) {
      if (piNodePolicy != null) {
        Pair pi = piNodePolicy.getPi(call, symbolTable);
        if (pi != null) {
          reuseOrCreatePi(pi.snd, pi.fst);
        }
      }
    }

    private void maybeInsertPi(SSAConditionalBranchInstruction cond) {
      if (piNodePolicy != null) {
        Pair pi = piNodePolicy.getPi(cond, getDef(cond.getUse(0)), getDef(cond.getUse(1)), symbolTable);
        if (pi != null) {
          reuseOrCreatePi(pi.snd, pi.fst);
        }
      }
    }

    private SSAInstruction getDef(int vn) {
      if (vn < creators.length) {
        return creators[vn];
      } else {
        return null;
      }
    }

    class EdgeVisitor extends com.ibm.wala.shrikeBT.IInstruction.Visitor {

      @Override
      public void visitInvoke(IInvokeInstruction instruction) {
        maybeInsertPi((SSAAbstractInvokeInstruction) getCurrentInstruction());
      }

      @Override
      public void visitConditionalBranch(IConditionalBranchInstruction instruction) {
        maybeInsertPi((SSAConditionalBranchInstruction) getCurrentInstruction());
      }
    }

    @Override
    public com.ibm.wala.shrikeBT.IInstruction[] getInstructions() {
      try {
        return shrikeCFG.getMethod().getInstructions();
      } catch (InvalidClassFileException e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
        return null;
      }
    }
  }

  /**
   * Build the IR
   */
  public void build() {
    solve();
    if (localMap != null) {
      localMap.finishLocalMap(this);
    }
  }

  public SSA2LocalMap getLocalMap() {
    return localMap;
  }

  public ShrikeIndirectionData getIndirectionData() {
    return ssaIndirections;
  }
  
  /**
   * A logical mapping from  -> local number Note: make sure this class remains static: this persists as part of
   * the IR!!
   */
  private static class SSA2LocalMap implements com.ibm.wala.ssa.IR.SSA2LocalMap {

    private final ShrikeCFG shrikeCFG;

    /**
     * Mapping Integer -> IntPair where p maps to (vn,L) iff we've started a range at pc p where value number vn corresponds to
     * local L
     */
    private final IntPair[] localStoreMap;

    /**
     * For each basic block i and local j, block2LocalState[i][j] gives the contents of local j at the start of block i
     */
    private final int[][] block2LocalState;

    /**
     * @param nInstructions number of instructions in the bytecode for this method
     * @param nBlocks number of basic blocks in the CFG
     */
    SSA2LocalMap(ShrikeCFG shrikeCfg, int nInstructions, int nBlocks) {
      shrikeCFG = shrikeCfg;
      localStoreMap = new IntPair[nInstructions];
      block2LocalState = new int[nBlocks][];
    }

    /**
     * Record the beginning of a new range, starting at the given program counter, in which a particular value number corresponds to
     * a particular local number
     */
    void startRange(int pc, int localNumber, int valueNumber) {
      localStoreMap[pc] = new IntPair(valueNumber, localNumber);
    }

    /**
     * Finish populating the map of local variable information
     */
    private void finishLocalMap(SSABuilder builder) {
      for (Iterator it = shrikeCFG.iterator(); it.hasNext();) {
        ShrikeCFG.BasicBlock bb = (ShrikeCFG.BasicBlock) it.next();
        MachineState S = builder.getIn(bb);
        int number = bb.getNumber();
        block2LocalState[number] = S.getLocals();
      }
    }

    /**
     * @param index - index into IR instruction array
     * @param vn - value number
     */
    public String[] getLocalNames(int index, int vn) {
      try {
        if (!shrikeCFG.getMethod().hasLocalVariableTable()) {
          return null;
        } else {
          int[] localNumbers = findLocalsForValueNumber(index, vn);
          if (localNumbers == null) {
            return null;
          } else {
            IBytecodeMethod m = shrikeCFG.getMethod();
            String[] result = new String[localNumbers.length];
            for (int i = 0; i < localNumbers.length; i++) {
              result[i] = m.getLocalVariableName(m.getBytecodeIndex(index), localNumbers[i]);
            }
            return result;
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
        return null;
      }
    }

    public int[] allocateNewLocalsArray(int maxLocals) {
      int[] result = new int[maxLocals];
      for (int i = 0; i < maxLocals; i++) {
        result[i] = OPTIMISTIC ? TOP : BOTTOM;
      }
      return result;
    }
    
    private int[] setLocal(int[] locals, int localNumber, int valueNumber) {
      if (locals == null) {
        locals = allocateNewLocalsArray(localNumber + 1);
      } else if (locals.length <= localNumber) {
        int[] newLocals = allocateNewLocalsArray(2 * Math.max(locals.length, localNumber) + 1);
        System.arraycopy(locals, 0, newLocals, 0, locals.length);
        locals = newLocals;
      }
      
      locals[localNumber] = valueNumber;
      
      return locals;
    }
    /**
     * @param pc a program counter (index into ShrikeBT instruction array)
     * @param vn a value number
     * @return if we know that immediately after the given program counter, v_vn corresponds to some set of locals, then return an
     *         array of the local numbers. else return null.
     */
    private int[] findLocalsForValueNumber(int pc, int vn) {
      if (vn < 0) {
        return null;
      }
      IBasicBlock bb = shrikeCFG.getBlockForInstruction(pc);
      int firstInstruction = bb.getFirstInstructionIndex();
      // walk forward from the first instruction to reconstruct the
      // state of the locals at this pc
      int[] locals = block2LocalState[bb.getNumber()];
      for (int i = firstInstruction; i <= pc; i++) {
        if (localStoreMap[i] != null) {
          IntPair p = localStoreMap[i];
          locals = setLocal(locals, p.getY(), p.getX());
        }
      }
      return locals == null ? null : extractIndices(locals, vn);
    }

    /**
     * @return the indices i s.t. x[i] == y, or null if none found.
     */
    private int[] extractIndices(int[] x, int y) {
      assert x != null;
      int count = 0;
      for (int i = 0; i < x.length; i++) {
        if (x[i] == y) {
          count++;
        }
      }
      if (count == 0) {
        return null;
      } else {
        int[] result = new int[count];
        int j = 0;
        for (int i = 0; i < x.length; i++) {
          if (x[i] == y) {
            result[j++] = i;
          }
        }
        return result;
      }
    }
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import java.util.Iterator;

import com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.cfg.ShrikeCFG;
import com.ibm.wala.cfg.ShrikeCFG.BasicBlock;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IBytecodeMethod;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.shrikeBT.ArrayLengthInstruction;
import com.ibm.wala.shrikeBT.ConstantInstruction;
import com.ibm.wala.shrikeBT.GotoInstruction;
import com.ibm.wala.shrikeBT.IArrayLoadInstruction;
import com.ibm.wala.shrikeBT.IArrayStoreInstruction;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IComparisonInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IConversionInstruction;
import com.ibm.wala.shrikeBT.IGetInstruction;
import com.ibm.wala.shrikeBT.IInstanceofInstruction;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrikeBT.ILoadIndirectInstruction;
import com.ibm.wala.shrikeBT.ILoadInstruction;
import com.ibm.wala.shrikeBT.IPutInstruction;
import com.ibm.wala.shrikeBT.IShiftInstruction;
import com.ibm.wala.shrikeBT.IStoreIndirectInstruction;
import com.ibm.wala.shrikeBT.IStoreInstruction;
import com.ibm.wala.shrikeBT.ITypeTestInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.shrikeBT.IndirectionData;
import com.ibm.wala.shrikeBT.MonitorInstruction;
import com.ibm.wala.shrikeBT.NewInstruction;
import com.ibm.wala.shrikeBT.ReturnInstruction;
import com.ibm.wala.shrikeBT.SwitchInstruction;
import com.ibm.wala.shrikeBT.ThrowInstruction;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.ShrikeIndirectionData.ShrikeLocalName;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.dominators.Dominators;
import com.ibm.wala.util.intset.IntPair;
import com.ibm.wala.util.shrike.ShrikeUtil;

/**
 * This class constructs an SSA {@link IR} from a backing ShrikeBT instruction stream.
 * 
 * The basic algorithm here is an abstract interpretation over the Java bytecode to determine types of stack locations and local
 * variables. As a side effect, the flow functions of the abstract interpretation emit instructions, eliminating the stack
 * abstraction and moving to a register-transfer language in SSA form.
 */
public class SSABuilder extends AbstractIntStackMachine {

  public static SSABuilder make(IBytecodeMethod method, SSACFG cfg, ShrikeCFG scfg, SSAInstruction[] instructions,
      SymbolTable symbolTable, boolean buildLocalMap, SSAPiNodePolicy piNodePolicy) throws IllegalArgumentException {
    if (scfg == null) {
      throw new IllegalArgumentException("scfg == null");
    }
    return new SSABuilder(method, cfg, scfg, instructions, symbolTable, buildLocalMap, piNodePolicy);
  }

  /**
   * A wrapper around the method being analyzed.
   */
  final private IBytecodeMethod method;

  /**
   * Governing symbol table
   */
  final private SymbolTable symbolTable;

  /**
   * A logical mapping from  -> local number if null, don't build it.
   */
  private final SSA2LocalMap localMap;

  /**
   * a factory to create concrete instructions
   */
  private final SSAInstructionFactory insts;

  /**
   * information about indirect use of local variables in the bytecode
   */
  private final IndirectionData bytecodeIndirections;
  
  private final ShrikeIndirectionData ssaIndirections;
  
      }
  private SSABuilder(IBytecodeMethod method, SSACFG cfg, ShrikeCFG scfg, SSAInstruction[] instructions, SymbolTable symbolTable,
      boolean buildLocalMap, SSAPiNodePolicy piNodePolicy) {
    super(scfg);
    localMap = buildLocalMap ? new SSA2LocalMap(scfg, instructions.length, cfg.getNumberOfNodes()) : null;
    init(new SymbolTableMeeter(symbolTable, cfg, scfg), new SymbolicPropagator(scfg, instructions, symbolTable,
        localMap, cfg, piNodePolicy));
    this.method = method;
    this.symbolTable = symbolTable;
    this.insts = method.getDeclaringClass().getClassLoader().getInstructionFactory();
    this.bytecodeIndirections = method.getIndirectionData();
    this.ssaIndirections = new ShrikeIndirectionData(instructions.length);
    assert cfg != null : "Null CFG";
  }

  private class SymbolTableMeeter implements Meeter {

    final SSACFG cfg;


    final SymbolTable symbolTable;

    final ShrikeCFG shrikeCFG;

    SymbolTableMeeter(SymbolTable symbolTable, SSACFG cfg, ShrikeCFG shrikeCFG) {
      this.cfg = cfg;
      this.symbolTable = symbolTable;
      this.shrikeCFG = shrikeCFG;
    }

    public int meetStack(int slot, int[] rhs, BasicBlock bb) {

      assert bb != null : "null basic block";

      if (bb.isExitBlock()) {
        return TOP;
      }

      if (allTheSame(rhs)) {
        for (int i = 0; i < rhs.length; i++) {
          if (rhs[i] != TOP) {
            return rhs[i];
          }
        }
        // didn't find anything but TOP
        return TOP;
      } else {
        SSACFG.BasicBlock newBB = cfg.getNode(shrikeCFG.getNumber(bb));
        // if we already have a phi for this stack location
        SSAPhiInstruction phi = newBB.getPhiForStackSlot(slot);
        int result;
        if (phi == null) {
          // no phi already exists. create one.
          result = symbolTable.newPhi(rhs);
          PhiValue v = symbolTable.getPhiValue(result);
          phi = v.getPhiInstruction();
          newBB.addPhiForStackSlot(slot, phi);
        } else {
          // already created a phi. update it to account for the
          // new merge.
          result = phi.getDef();
          phi.setValues(rhs.clone());
        }
        return result;
      }
    }

    /**
     * @see com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine.Meeter#meetLocal(int, int[], BasicBlock)
     */
    public int meetLocal(int n, int[] rhs, BasicBlock bb) {
      if (allTheSame(rhs)) {
        for (int i = 0; i < rhs.length; i++) {
          if (rhs[i] != TOP) {
            return rhs[i];
          }
        }
        // didn't find anything but TOP
        return TOP;
      } else {
        SSACFG.BasicBlock newBB = cfg.getNode(shrikeCFG.getNumber(bb));
        if (bb.isExitBlock()) {
          // no phis in exit block please
          return TOP;
        }
        // if we already have a phi for this local
        SSAPhiInstruction phi = newBB.getPhiForLocal(n);
        int result;
        if (phi == null) {
          // no phi already exists. create one.
          result = symbolTable.newPhi(rhs);
          PhiValue v = symbolTable.getPhiValue(result);
          phi = v.getPhiInstruction();
          newBB.addPhiForLocal(n, phi);
        } else {
          // already created a phi. update it to account for the
          // new merge.
          result = phi.getDef();
          phi.setValues(rhs.clone());
        }
        return result;
    }

    /**
     * Are all rhs values all the same? Note, we consider TOP (-1) to be same as everything else.
     * 
     * @param rhs
     * @return boolean
     */
    private boolean allTheSame(int[] rhs) {
      int x = -1;
      // set x := the first non-TOP value
      int i = 0;
      for (i = 0; i < rhs.length; i++) {
        if (rhs[i] != TOP) {
          x = rhs[i];
          break;
        }
      }
      // check the remaining values
      for (i = i + 1; i < rhs.length; i++) {
        if (rhs[i] != x && rhs[i] != TOP)
          return false;
      }
      return true;
    }

    /**
     * @see com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine.Meeter#meetStackAtCatchBlock(BasicBlock)
     */
    public int meetStackAtCatchBlock(BasicBlock bb) {
      int bbNumber = shrikeCFG.getNumber(bb);
      SSACFG.ExceptionHandlerBasicBlock newBB = (SSACFG.ExceptionHandlerBasicBlock) cfg.getNode(bbNumber);
      SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction();
      int exceptionValue;
      if (s == null) {
        exceptionValue = symbolTable.newSymbol();
        s = insts.GetCaughtExceptionInstruction(SSAInstruction.NO_INDEX, bbNumber, exceptionValue);
        newBB.setCatchInstruction(s);
      } else {
        exceptionValue = s.getException();
      }
      return exceptionValue;

    }
  }

  @Override
  protected void initializeVariables() {
    MachineState entryState = getEntryState();
    int parameterNumber = 0;
    int local = -1;
    for (int i = 0; i < method.getNumberOfParameters(); i++) {
      local++;
      TypeReference t = method.getParameterType(i);
      if (t != null) {
        int symbol = symbolTable.getParameter(parameterNumber++);
        entryState.setLocal(local, symbol);
        if (t.equals(TypeReference.Double) || t.equals(TypeReference.Long)) {
          local++;
        }
      }
    }

    // This useless value ensures that the state cannot be empty, even
    // for a static method with no arguments in blocks with an empty stack
    // and no locals being used. This ensures that propagation of the
    // state thru the CFGSystem will always show changes the first time
    // it reaches a block, and thus no piece of the CFG will be skipped.
    //
    // (note that this bizarre state really happened, in java_cup)
    //
    // SJF: I don't understand how this is supposed to work. It
    // causes a bug right now in normal cases, so I'm commenting it out
    // for now. If there's a problem, let's add a regression test
    // to catch it.
    //
    entryState.push(symbolTable.newSymbol());
  }

  /**
   * This class defines the type abstractions for this analysis and the flow function for each instruction in the ShrikeBT IR.
   */
  private class SymbolicPropagator extends BasicStackFlowProvider {

    final SSAInstruction[] instructions;

    final SymbolTable symbolTable;

    final ShrikeCFG shrikeCFG;

    final SSACFG cfg;

    final ClassLoaderReference loader;

    /**
     * creators[i] holds the instruction that defs value number i
     */
    private SSAInstruction[] creators;

    final SSA2LocalMap localMap;

    final SSAPiNodePolicy piNodePolicy;

        } else {
    public SymbolicPropagator(ShrikeCFG shrikeCFG, SSAInstruction[] instructions, SymbolTable symbolTable, SSA2LocalMap localMap,
        SSACFG cfg, SSAPiNodePolicy piNodePolicy) {
      super(shrikeCFG);
      this.piNodePolicy = piNodePolicy;
      this.cfg = cfg;
      this.creators = new SSAInstruction[0];
      this.shrikeCFG = shrikeCFG;
      this.instructions = instructions;
      this.symbolTable = symbolTable;
      this.loader = shrikeCFG.getMethod().getDeclaringClass().getClassLoader().getReference();
      this.localMap = localMap;
      init(this.new NodeVisitor(), this.new EdgeVisitor());
    }

    @Override
    public boolean needsEdgeFlow() {
      return piNodePolicy != null;
    }

    private void emitInstruction(SSAInstruction s) {
      if (s != null) {
        instructions[getCurrentInstructionIndex()] = s;
        for (int i = 0; i < s.getNumberOfDefs(); i++) {
          if (creators.length < (s.getDef(i) + 1)) {
            SSAInstruction[] arr = new SSAInstruction[2 * s.getDef(i)];
            System.arraycopy(creators, 0, arr, 0, creators.length);
            creators = arr;
          }

          assert s.getDef(i) != -1 : "invalid def " + i + " for " + s;
        
          creators[s.getDef(i)] = s;
        }
      }
    }

    private SSAInstruction getCurrentInstruction() {
      return instructions[getCurrentInstructionIndex()];
    }

    /**
     * If we've already created the current instruction, return the value number def'ed by the current instruction. Else, create a
     * new symbol.
     */
    private int reuseOrCreateDef() {
      if (getCurrentInstruction() == null) {
        return symbolTable.newSymbol();
      } else {
        return getCurrentInstruction().getDef();
      }
    }

    /**
     * If we've already created the current instruction, return the value number representing the exception the instruction may
     * throw. Else, create a new symbol
     */
    private int reuseOrCreateException() {

      if (getCurrentInstruction() != null) {
        assert getCurrentInstruction() instanceof SSAInvokeInstruction;
      }
      if (getCurrentInstruction() == null) {
        return symbolTable.newSymbol();
      } else {
        SSAInvokeInstruction s = (SSAInvokeInstruction) getCurrentInstruction();
        return s.getException();
      }
    }

    /**
     * Update the machine state to account for an instruction
     */
    class NodeVisitor extends BasicStackMachineVisitor {

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLength(ArrayLengthInstruction)
       */
      @Override
      public void visitArrayLength(com.ibm.wala.shrikeBT.ArrayLengthInstruction instruction) {

        int arrayRef = workingState.pop();
        int length = reuseOrCreateDef();

        workingState.push(length);
        emitInstruction(insts.ArrayLengthInstruction(getCurrentInstructionIndex(), length, arrayRef));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLoad(IArrayLoadInstruction)
       */
      @Override
      public void visitArrayLoad(IArrayLoadInstruction instruction) {
        int index = workingState.pop();
        int arrayRef = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        if (instruction.isAddressOf()) {
        for (int i = n - 1; i >= 0; i--) {
          emitInstruction(insts.AddressOfInstruction(getCurrentInstructionIndex(), result, arrayRef, index, t));
        } else {
           emitInstruction(insts.ArrayLoadInstruction(getCurrentInstructionIndex(), result, arrayRef, index, t));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayStore(IArrayStoreInstruction)
       */
      @Override
      public void visitArrayStore(IArrayStoreInstruction instruction) {

        int value = workingState.pop();
        int index = workingState.pop();
        int arrayRef = workingState.pop();
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.ArrayStoreInstruction(getCurrentInstructionIndex(), arrayRef, index, value, t));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitBinaryOp(IBinaryOpInstruction)
       */
      @Override
      public void visitBinaryOp(IBinaryOpInstruction instruction) {
        int val2 = workingState.pop();
        int val1 = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        boolean isFloat = instruction.getType().equals(TYPE_double) || instruction.getType().equals(TYPE_float);
        emitInstruction(insts.BinaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), instruction.throwsExceptionOnOverflow(), instruction
            .isUnsigned(), result, val1, val2, !isFloat));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitCheckCast(ITypeTestInstruction)
       */
      @Override
      public void visitCheckCast(ITypeTestInstruction instruction) {
        int val = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        if (! instruction.firstClassTypes()) {
          String[] typeNames = instruction.getTypes();
          TypeReference[] t = new TypeReference[ typeNames.length ];
          for(int i = 0; i < typeNames.length; i++) {
            t[i] = ShrikeUtil.makeTypeReference(loader, typeNames[i]);
          }
          emitInstruction(insts.CheckCastInstruction(getCurrentInstructionIndex(), result, val, t, instruction.isPEI()));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitComparison(IComparisonInstruction)
       */
      @Override
      public void visitComparison(IComparisonInstruction instruction) {

        int val2 = workingState.pop();
        int val1 = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        emitInstruction(insts.ComparisonInstruction(getCurrentInstructionIndex(), instruction.getOperator(), result, val1, val2));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConditionalBranch(IConditionalBranchInstruction)
       */
      @Override
      public void visitConditionalBranch(IConditionalBranchInstruction instruction) {
        int val2 = workingState.pop();
        int val1 = workingState.pop();

        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.ConditionalBranchInstruction(getCurrentInstructionIndex(), instruction.getOperator(), t, val1, val2));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConstant(ConstantInstruction)
       */
      @Override
      public void visitConstant(com.ibm.wala.shrikeBT.ConstantInstruction instruction) {
        Language l = cfg.getMethod().getDeclaringClass().getClassLoader().getLanguage();
        TypeReference type = l.getConstantType(instruction.getValue());
        int symbol = 0;
        if (l.isNullType(type)) {
          symbol = symbolTable.getNullConstant();
        } else if (l.isIntType(type)) {
          Integer value = (Integer) instruction.getValue();
          params[i] = workingState.pop();
          symbol = symbolTable.getConstant(value.intValue());
        } else if (l.isLongType(type)) {
          Long value = (Long) instruction.getValue();
          symbol = symbolTable.getConstant(value.longValue());
        } else if (l.isFloatType(type)) {
          Float value = (Float) instruction.getValue();
          symbol = symbolTable.getConstant(value.floatValue());
        } else if (l.isDoubleType(type)) {
          Double value = (Double) instruction.getValue();
          symbol = symbolTable.getConstant(value.doubleValue());
        } else if (l.isStringType(type)) {
          String value = (String) instruction.getValue();
        } else {
          symbol = symbolTable.getConstant(value);
        } else if (l.isMetadataType(type)) {
          Object rval = l.getMetadataToken(instruction.getValue());
          symbol = reuseOrCreateDef();
          emitInstruction(insts.LoadMetadataInstruction(getCurrentInstructionIndex(), symbol, type, rval));
        } else {
          Assertions.UNREACHABLE("unexpected " + type);
        }
        workingState.push(symbol);
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConversion(IConversionInstruction)
       */
      @Override
      public void visitConversion(IConversionInstruction instruction) {

        int val = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);

        TypeReference fromType = ShrikeUtil.makeTypeReference(loader, instruction.getFromType());
        TypeReference toType = ShrikeUtil.makeTypeReference(loader, instruction.getToType());

        emitInstruction(insts.ConversionInstruction(getCurrentInstructionIndex(), result, val, fromType, toType, instruction.throwsExceptionOnOverflow()));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGet(IGetInstruction)
       */
      @Override
      public void visitGet(IGetInstruction instruction) {
        int result = reuseOrCreateDef();
        FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(),
            instruction.getFieldType());
        if (instruction.isAddressOf()) {
          int ref = instruction.isStatic()? -1: workingState.pop();
          emitInstruction(insts.AddressOfInstruction(getCurrentInstructionIndex(), result, ref, f, f.getFieldType()));
        } else if (instruction.isStatic()) {
          emitInstruction(insts.GetInstruction(getCurrentInstructionIndex(), result, f));
        } else {
          int ref = workingState.pop();
          emitInstruction(insts.GetInstruction(getCurrentInstructionIndex(), result, ref, f));
        }
        workingState.push(result);
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGoto(GotoInstruction)
       */
      @Override
      public void visitGoto(com.ibm.wala.shrikeBT.GotoInstruction instruction) {
        emitInstruction(insts.GotoInstruction(getCurrentInstructionIndex()));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitInstanceof(IInstanceofInstruction)
       */
      @Override
      public void visitInstanceof(IInstanceofInstruction instruction) {

        int ref = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.InstanceofInstruction(getCurrentInstructionIndex(), result, ref, t));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitInvoke(IInvokeInstruction)
       */
      @Override
      public void visitInvoke(IInvokeInstruction instruction) {
        doIndirectReads(bytecodeIndirections.indirectlyReadLocals(getCurrentInstructionIndex()));
        int n = instruction.getPoppedCount();
        int[] params = new int[n];
        }
        Language lang = shrikeCFG.getMethod().getDeclaringClass().getClassLoader().getLanguage();
        MethodReference m = MethodReference.findOrCreate(lang, loader, instruction.getClassType(), instruction.getMethodName(),
            instruction.getMethodSignature());
        IInvokeInstruction.IDispatch code = instruction.getInvocationCode();
        CallSiteReference site = CallSiteReference.make(getCurrentProgramCounter(), m, code);
        int exc = reuseOrCreateException();
        if (instruction.getPushedWordSize() > 0) {
          int result = reuseOrCreateDef();
          workingState.push(result);
          emitInstruction(insts.InvokeInstruction(getCurrentInstructionIndex(), result, params, exc, site));
          emitInstruction(insts.InvokeInstruction(getCurrentInstructionIndex(), params, exc, site));
        }
        doIndirectWrites(bytecodeIndirections.indirectlyWrittenLocals(getCurrentInstructionIndex()), -1);
      }

      @Override
      public void visitLocalLoad(ILoadInstruction instruction) {
        if (instruction.isAddressOf()) {
          int result = reuseOrCreateDef();
 
          int t = workingState.getLocal(instruction.getVarIndex()); 
          if (t == -1) {
            doIndirectWrites(new int[]{instruction.getVarIndex()}, -1);
            t = workingState.getLocal(instruction.getVarIndex());
          }
          
          TypeReference type = ShrikeUtil.makeTypeReference(loader, instruction.getType());
          emitInstruction(insts.AddressOfInstruction(getCurrentInstructionIndex(), result, t, type));
          workingState.push(result);
        } else {
          super.visitLocalLoad(instruction);
        }
      }

      /*
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitLocalStore(com.ibm.wala.shrikeBT.StoreInstruction)
       */
      @Override
      public void visitLocalStore(IStoreInstruction instruction) {
        if (localMap != null) {
          localMap.startRange(getCurrentInstructionIndex(), instruction.getVarIndex(), workingState.peek());
        }
        super.visitLocalStore(instruction);
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitMonitor(MonitorInstruction)
       */
      @Override
      public void visitMonitor(com.ibm.wala.shrikeBT.MonitorInstruction instruction) {

        int ref = workingState.pop();
        emitInstruction(insts.MonitorInstruction(getCurrentInstructionIndex(), ref, instruction.isEnter()));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitNew(NewInstruction)
       */
      @Override
      public void visitNew(com.ibm.wala.shrikeBT.NewInstruction instruction) {
        int result = reuseOrCreateDef();
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        NewSiteReference ref = NewSiteReference.make(getCurrentProgramCounter(), t);
        if (t.isArrayType()) {
          int[] sizes = new int[instruction.getArrayBoundsCount()];
          for (int i = 0; i < instruction.getArrayBoundsCount(); i++) {
            sizes[instruction.getArrayBoundsCount() - 1 - i] = workingState.pop();
          }
          emitInstruction(insts.NewInstruction(getCurrentInstructionIndex(), result, ref, sizes));
        } else {
          emitInstruction(insts.NewInstruction(getCurrentInstructionIndex(), result, ref));
          popN(instruction);
        }
        workingState.push(result);
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGet(IGetInstruction)
       */
      @Override
      public void visitPut(IPutInstruction instruction) {
        int value = workingState.pop();
        if (instruction.isStatic()) {
          FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(),
              instruction.getFieldType());
          emitInstruction(insts.PutInstruction(getCurrentInstructionIndex(), value, f));
          int ref = workingState.pop();
          FieldReference f = FieldReference.findOrCreate(loader, instruction.getClassType(), instruction.getFieldName(),
              instruction.getFieldType());
          emitInstruction(insts.PutInstruction(getCurrentInstructionIndex(), ref, value, f));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitReturn(ReturnInstruction)
       */
      @Override
      public void visitReturn(com.ibm.wala.shrikeBT.ReturnInstruction instruction) {
        if (instruction.getPoppedCount() == 1) {
          int result = workingState.pop();
          TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
          emitInstruction(insts.ReturnInstruction(getCurrentInstructionIndex(), result, t.isPrimitiveType()));
        } else {
          emitInstruction(insts.ReturnInstruction(getCurrentInstructionIndex()));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitShift(IShiftInstruction)
       */
      @Override
      public void visitShift(IShiftInstruction instruction) {
        int val2 = workingState.pop();
        int val1 = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        emitInstruction(insts.BinaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), false, instruction.isUnsigned(), result, val1, val2,
            true));
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitSwitch(SwitchInstruction)
       */
      @Override
      public void visitSwitch(com.ibm.wala.shrikeBT.SwitchInstruction instruction) {
        int val = workingState.pop();
        emitInstruction(insts.SwitchInstruction(getCurrentInstructionIndex(), val, instruction.getDefaultLabel(), instruction.getCasesAndLabels()));
      }

      private Dominators dom = null;

      private int findRethrowException() {
        int index = getCurrentInstructionIndex();
        SSACFG.BasicBlock bb = cfg.getBlockForInstruction(index);
        if (bb.isCatchBlock()) {
          SSACFG.ExceptionHandlerBasicBlock newBB = (SSACFG.ExceptionHandlerBasicBlock) bb;
          SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction();
          return s.getDef();
        } else {
          // TODO: should we really use dominators here? maybe it would be cleaner to propagate
          // the notion of 'current exception to rethrow' using the abstract interpreter.
          if (dom == null) {
            dom = Dominators.make(cfg, cfg.entry());
          }

          ISSABasicBlock x = bb;
          while (x != null) {
            if (x.isCatchBlock()) {
              SSACFG.ExceptionHandlerBasicBlock newBB = (SSACFG.ExceptionHandlerBasicBlock) x;
              SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction();
              return s.getDef();
            } else {
              x = dom.getIdom(x);
            }
          }

          // assert false;
          return -1;
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitThrow(ThrowInstruction)
       */
      @Override
      public void visitThrow(com.ibm.wala.shrikeBT.ThrowInstruction instruction) {
        if (instruction.isRethrow()) {
          workingState.clearStack();
          emitInstruction(insts.ThrowInstruction(getCurrentInstructionIndex(), findRethrowException()));
        } else {
          int exception = workingState.pop();
          workingState.clearStack();
          workingState.push(exception);
          emitInstruction(insts.ThrowInstruction(getCurrentInstructionIndex(), exception));
        }
      }

      /**
       * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitUnaryOp(IUnaryOpInstruction)
       */
      @Override
      public void visitUnaryOp(IUnaryOpInstruction instruction) {
        int val = workingState.pop();
        int result = reuseOrCreateDef();
        workingState.push(result);
        emitInstruction(insts.UnaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), result, val));
      }

      private void doIndirectReads(int[] locals) {
        for(int i = 0; i < locals.length; i++) {
          ssaIndirections.setUse(getCurrentInstructionIndex(), new ShrikeLocalName(locals[i]), workingState.getLocal(locals[i]));
        }
      }
      
      @Override
      public void visitLoadIndirect(ILoadIndirectInstruction instruction) { 
        int addressVal = workingState.pop();
        int result = reuseOrCreateDef();
        doIndirectReads(bytecodeIndirections.indirectlyReadLocals(getCurrentInstructionIndex()));
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getPushedType(null));
        emitInstruction(insts.LoadIndirectInstruction(getCurrentInstructionIndex(), result, t, addressVal));
        workingState.push(result);
      }

      private void doIndirectWrites(int[] locals, int rval) {
        for(int i = 0; i < locals.length; i++) {
          ShrikeLocalName name = new ShrikeLocalName(locals[i]);
          int idx = getCurrentInstructionIndex();
          if (ssaIndirections.getDef(idx, name) == -1) {
            ssaIndirections.setDef(idx, name, rval==-1? symbolTable.newSymbol(): rval);
          }
          workingState.setLocal(locals[i], ssaIndirections.getDef(idx, name));
        }        
      }
      
      @Override
      public void visitStoreIndirect(IStoreIndirectInstruction instruction) {  
        int val = workingState.pop();        
        int addressVal = workingState.pop();
        doIndirectWrites(bytecodeIndirections.indirectlyWrittenLocals(getCurrentInstructionIndex()), val);     
        TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
        emitInstruction(insts.StoreIndirectInstruction(getCurrentInstructionIndex(), addressVal, val, t));
      }

    }

    /**
     * @param piCause
     * @param ref
     */
    private void reuseOrCreatePi(SSAInstruction piCause, int ref) {
      int n = getCurrentInstructionIndex();
      SSACFG.BasicBlock bb = cfg.getBlockForInstruction(n);

      BasicBlock path = getCurrentSuccessor();
      int outNum = shrikeCFG.getNumber(path);

      SSAPiInstruction pi = bb.getPiForRefAndPath(ref, path);
      if (pi == null) {
        pi = insts.PiInstruction(SSAInstruction.NO_INDEX, symbolTable.newSymbol(), ref, bb.getNumber(), outNum, piCause);
        bb.addPiForRefAndPath(ref, path, pi);
      }

      workingState.replaceValue(ref, pi.getDef());
    }

    // private void maybeInsertPi(int val) {
    // if ((addPiForFieldSelect) && (creators.length > val) && (creators[val] instanceof SSAGetInstruction)
    // && !((SSAGetInstruction) creators[val]).isStatic()) {
    // reuseOrCreatePi(creators[val], val);
    // } else if ((addPiForDispatchSelect)
    // && (creators.length > val)
    // && (creators[val] instanceof SSAInvokeInstruction)
    // && (((SSAInvokeInstruction) creators[val]).getInvocationCode() == IInvokeInstruction.Dispatch.VIRTUAL ||
    // ((SSAInvokeInstruction) creators[val])
    // .getInvocationCode() == IInvokeInstruction.Dispatch.INTERFACE)) {
    // reuseOrCreatePi(creators[val], val);
    // }
    // }

    private void maybeInsertPi(SSAAbstractInvokeInstruction call) {
      if (piNodePolicy != null) {
        Pair pi = piNodePolicy.getPi(call, symbolTable);
        if (pi != null) {
          reuseOrCreatePi(pi.snd, pi.fst);
        }
      }
    }

    private void maybeInsertPi(SSAConditionalBranchInstruction cond) {
      if (piNodePolicy != null) {
        Pair pi = piNodePolicy.getPi(cond, getDef(cond.getUse(0)), getDef(cond.getUse(1)), symbolTable);
        if (pi != null) {
          reuseOrCreatePi(pi.snd, pi.fst);
        }
      }
    }

    private SSAInstruction getDef(int vn) {
      if (vn < creators.length) {
        return creators[vn];
      } else {
        return null;
      }
    }

    class EdgeVisitor extends com.ibm.wala.shrikeBT.IInstruction.Visitor {

      @Override
      public void visitInvoke(IInvokeInstruction instruction) {
        maybeInsertPi((SSAAbstractInvokeInstruction) getCurrentInstruction());
      }

      @Override
      public void visitConditionalBranch(IConditionalBranchInstruction instruction) {
        maybeInsertPi((SSAConditionalBranchInstruction) getCurrentInstruction());
      }
    }

    @Override
    public com.ibm.wala.shrikeBT.IInstruction[] getInstructions() {
      try {
        return shrikeCFG.getMethod().getInstructions();
      } catch (InvalidClassFileException e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
        return null;
      }
    }
  }

  /**
   * Build the IR
   */
  public void build() {
    solve();
    if (localMap != null) {
      localMap.finishLocalMap(this);
    }
  }

  public SSA2LocalMap getLocalMap() {
    return localMap;
  }

  public ShrikeIndirectionData getIndirectionData() {
    return ssaIndirections;
  }
  
  /**
   * A logical mapping from  -> local number Note: make sure this class remains static: this persists as part of
   * the IR!!
   */
  private static class SSA2LocalMap implements com.ibm.wala.ssa.IR.SSA2LocalMap {

    private final ShrikeCFG shrikeCFG;

    /**
     * Mapping Integer -> IntPair where p maps to (vn,L) iff we've started a range at pc p where value number vn corresponds to
     * local L
     */
    private final IntPair[] localStoreMap;

    /**
     * For each basic block i and local j, block2LocalState[i][j] gives the contents of local j at the start of block i
     */
    private final int[][] block2LocalState;

    /**
     * @param nInstructions number of instructions in the bytecode for this method
     * @param nBlocks number of basic blocks in the CFG
     */
    SSA2LocalMap(ShrikeCFG shrikeCfg, int nInstructions, int nBlocks) {
      shrikeCFG = shrikeCfg;
      localStoreMap = new IntPair[nInstructions];
      block2LocalState = new int[nBlocks][];
    }

    /**
     * Record the beginning of a new range, starting at the given program counter, in which a particular value number corresponds to
     * a particular local number
     */
    void startRange(int pc, int localNumber, int valueNumber) {
      localStoreMap[pc] = new IntPair(valueNumber, localNumber);
    }

    /**
     * Finish populating the map of local variable information
     */
    private void finishLocalMap(SSABuilder builder) {
      for (Iterator it = shrikeCFG.iterator(); it.hasNext();) {
        ShrikeCFG.BasicBlock bb = (ShrikeCFG.BasicBlock) it.next();
        MachineState S = builder.getIn(bb);
        int number = bb.getNumber();
        block2LocalState[number] = S.getLocals();
      }
    }

    /**
     * @param index - index into IR instruction array
     * @param vn - value number
     */
    public String[] getLocalNames(int index, int vn) {
      try {
        if (!shrikeCFG.getMethod().hasLocalVariableTable()) {
          return null;
        } else {
          int[] localNumbers = findLocalsForValueNumber(index, vn);
          if (localNumbers == null) {
            return null;
          } else {
            IBytecodeMethod m = shrikeCFG.getMethod();
            String[] result = new String[localNumbers.length];
            for (int i = 0; i < localNumbers.length; i++) {
              result[i] = m.getLocalVariableName(m.getBytecodeIndex(index), localNumbers[i]);
            }
            return result;
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
        return null;
      }
    }

    public int[] allocateNewLocalsArray(int maxLocals) {
      int[] result = new int[maxLocals];
      for (int i = 0; i < maxLocals; i++) {
        result[i] = OPTIMISTIC ? TOP : BOTTOM;
      }
      return result;
    }
    
    private int[] setLocal(int[] locals, int localNumber, int valueNumber) {
      if (locals == null) {
        locals = allocateNewLocalsArray(localNumber + 1);
      } else if (locals.length <= localNumber) {
        int[] newLocals = allocateNewLocalsArray(2 * Math.max(locals.length, localNumber) + 1);
        System.arraycopy(locals, 0, newLocals, 0, locals.length);
        locals = newLocals;
      }
      
      locals[localNumber] = valueNumber;
      
      return locals;
    }
    /**
     * @param pc a program counter (index into ShrikeBT instruction array)
     * @param vn a value number
     * @return if we know that immediately after the given program counter, v_vn corresponds to some set of locals, then return an
     *         array of the local numbers. else return null.
     */
    private int[] findLocalsForValueNumber(int pc, int vn) {
      if (vn < 0) {
        return null;
      }
      IBasicBlock bb = shrikeCFG.getBlockForInstruction(pc);
      int firstInstruction = bb.getFirstInstructionIndex();
      // walk forward from the first instruction to reconstruct the
      // state of the locals at this pc
      int[] locals = block2LocalState[bb.getNumber()];
      for (int i = firstInstruction; i <= pc; i++) {
        if (localStoreMap[i] != null) {
          IntPair p = localStoreMap[i];
          locals = setLocal(locals, p.getY(), p.getX());
        }
      }
      return locals == null ? null : extractIndices(locals, vn);
    }

    /**
     * @return the indices i s.t. x[i] == y, or null if none found.
     */
    private int[] extractIndices(int[] x, int y) {
      assert x != null;
      int count = 0;
      for (int i = 0; i < x.length; i++) {
        if (x[i] == y) {
          count++;
        }
      }
      if (count == 0) {
        return null;
      } else {
        int[] result = new int[count];
        int j = 0;
        for (int i = 0; i < x.length; i++) {
          if (x[i] == y) {
            result[j++] = i;
          }
        }
        return result;
      }
    }
  }
}
File
SSABuilder.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
  }
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**

 * A checkcast (dynamic type test) instruction. This instruction produces a new value number (like an assignment) if the check
 * succeeds.
 * 
 * Note that this instruction generalizes the meaning of checkcast in Java since it supports
 * multiple types for which to check.  The meaning is that the case succeeds if the object
 * is of any of the desired types.
 *  
 */
public abstract class SSACheckCastInstruction extends SSAInstruction {

  /**
   * A new value number def'fed by this instruction when the type check succeeds.
   */
  private final int result;

  /**
   * The value being checked by this instruction
   */
  private final int val;

  /**
   * The types for which this instruction checks; the assignment succeeds if the val is a subtype of one of these types
   */
  private final TypeReference[] declaredResultTypes;

  /**
   * whether the type test throws an exception
   */
  private final boolean isPEI;
  
  /**
   * @param result A new value number def'fed by this instruction when the type check succeeds.
   * @param val The value being checked by this instruction
   * @param type The type which this instruction checks
   */
  protected SSACheckCastInstruction(int iindex, int result, int val, TypeReference[] types, boolean isPEI) {
    super(iindex);
    this.result = result;
    this.val = val;
    this.declaredResultTypes = types;
    this.isPEI = isPEI;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("(defs != null) and (defs.length == 0)");
    }
    if (uses != null && uses.length == 0) {
      throw new IllegalArgumentException("(uses != null) and (uses.length == 0)");
    }
    return insts.CheckCastInstruction(iindex, defs == null ? result : defs[0], uses == null ? val : uses[0], declaredResultTypes, isPEI);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    String v = getValueString(symbolTable, result) + " = checkcast";
    for (TypeReference t : declaredResultTypes) {
        v = v + " " + t;
    }
    return v + getValueString(symbolTable, val);
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * 
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitCheckCast(this);
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#hasDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  /**
   * @return A new value number def'fed by this instruction when the type check succeeds.
   */
  @Override
  public int getDef() {
    return result;
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getDef(int)
   */
  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return val;
  }

  /**
   * @deprecated the system now supports multiple types, so this
   * accessor will not work for all languages.
   */
  @Deprecated
  public TypeReference getDeclaredResultType() {
    assert declaredResultTypes.length == 1;
    return declaredResultTypes[0];
  }

  public TypeReference[] getDeclaredResultTypes() {
    return declaredResultTypes;
  }

  public int getResult() {
    return result;
  }

  public int getVal() {
    return val;
  }

  @Override
  public int hashCode() {
    return result * 7529 + val;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return isPEI;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  @Override
  public String toString() {
    String s = super.toString();
    for (TypeReference t : declaredResultTypes) {
      s = s + " " + t;
    }
    return s;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * A checkcast (dynamic type test) instruction. This instruction produces a new value number (like an assignment) if the check
 * succeeds.
 * 
 * Note that this instruction generalizes the meaning of checkcast in Java since it supports
 * multiple types for which to check.  The meaning is that the case succeeds if the object
 * is of any of the desired types.
 *  
 */
public abstract class SSACheckCastInstruction extends SSAInstruction {

  /**
   * A new value number def'fed by this instruction when the type check succeeds.
   */
  private final int result;

  /**
   * The value being checked by this instruction
   */
  private final int val;

  /**
   * The types for which this instruction checks; the assignment succeeds if the val is a subtype of one of these types
   */
  private final TypeReference[] declaredResultTypes;

  /**
   * whether the type test throws an exception
   */
  private final boolean isPEI;
  
  /**
   * @param result A new value number def'fed by this instruction when the type check succeeds.
   * @param val The value being checked by this instruction
   * @param type The type which this instruction checks
   */
  protected SSACheckCastInstruction(int result, int val, TypeReference[] types, boolean isPEI) {
    super();
    this.result = result;
    this.val = val;
    this.declaredResultTypes = types;
    this.isPEI = isPEI;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("(defs != null) and (defs.length == 0)");
    }
    if (uses != null && uses.length == 0) {
      throw new IllegalArgumentException("(uses != null) and (uses.length == 0)");
    }
    return insts.CheckCastInstruction(defs == null ? result : defs[0], uses == null ? val : uses[0], declaredResultTypes, isPEI);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    String v = getValueString(symbolTable, result) + " = checkcast";
    for (TypeReference t : declaredResultTypes) {
        v = v + " " + t;
    }
    return v + getValueString(symbolTable, val);
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * 
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitCheckCast(this);
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#hasDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  /**
   * @return A new value number def'fed by this instruction when the type check succeeds.
   */
  @Override
  public int getDef() {
    return result;
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getDef(int)
  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return val;
  }

  /**
   * @deprecated the system now supports multiple types, so this
   * accessor will not work for all languages.
   */
  @Deprecated
  public TypeReference getDeclaredResultType() {
    assert declaredResultTypes.length == 1;
    return declaredResultTypes[0];
  }

  public TypeReference[] getDeclaredResultTypes() {
    return declaredResultTypes;
  }

  public int getResult() {
    return result;
  }

  public int getVal() {
    return val;
  }

  @Override
  public int hashCode() {
    return result * 7529 + val;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return isPEI;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  @Override
  public String toString() {
    String s = super.toString();
    for (TypeReference t : declaredResultTypes) {
      s = s + " " + t;
    }
    return s;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * A checkcast (dynamic type test) instruction. This instruction produces a new value number (like an assignment) if the check
 * succeeds.
 * 
 * Note that this instruction generalizes the meaning of checkcast in Java since it supports
 * multiple types for which to check.  The meaning is that the case succeeds if the object
 * is of any of the desired types.
 *  
 */
public abstract class SSACheckCastInstruction extends SSAInstruction {

  /**
   * A new value number def'fed by this instruction when the type check succeeds.
   */
  private final int result;

  /**
   * The value being checked by this instruction
   */
  private final int val;

  /**
   * The types for which this instruction checks; the assignment succeeds if the val is a subtype of one of these types
   */
  private final TypeReference[] declaredResultTypes;

  /**
   * whether the type test throws an exception
   */
  private final boolean isPEI;
  
  /**
   * @param result A new value number def'fed by this instruction when the type check succeeds.
   * @param val The value being checked by this instruction
   * @param type The type which this instruction checks
   */
  protected SSACheckCastInstruction(int iindex, int result, int val, TypeReference[] types, boolean isPEI) {
    super(iindex);
    this.result = result;
    this.val = val;
    this.declaredResultTypes = types;
    this.isPEI = isPEI;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("(defs != null) and (defs.length == 0)");
    }
    if (uses != null && uses.length == 0) {
      throw new IllegalArgumentException("(uses != null) and (uses.length == 0)");
    }
    return insts.CheckCastInstruction(iindex, defs == null ? result : defs[0], uses == null ? val : uses[0], declaredResultTypes, isPEI);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    String v = getValueString(symbolTable, result) + " = checkcast";
    for (TypeReference t : declaredResultTypes) {
        v = v + " " + t;
    }
    return v + getValueString(symbolTable, val);
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * 
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitCheckCast(this);
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#hasDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  /**
   * @return A new value number def'fed by this instruction when the type check succeeds.
   */
  @Override
  public int getDef() {
    return result;
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getDef(int)
   */
  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /*
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return val;
  }

  /**
   * @deprecated the system now supports multiple types, so this
   * accessor will not work for all languages.
   */
  @Deprecated
  public TypeReference getDeclaredResultType() {
    assert declaredResultTypes.length == 1;
    return declaredResultTypes[0];
  }

  public TypeReference[] getDeclaredResultTypes() {
    return declaredResultTypes;
  }

  public int getResult() {
    return result;
  }

  public int getVal() {
    return val;
  }

  @Override
  public int hashCode() {
    return result * 7529 + val;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return isPEI;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  @Override
  public String toString() {
    String s = super.toString();
    for (TypeReference t : declaredResultTypes) {
      s = s + " " + t;
    }
    return s;
  }

}
File
SSACheckCastInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.IComparisonInstruction;

/**
 * SSA Instruction for comparisons between floats, longs and doubles
 */
public class SSAComparisonInstruction extends SSAInstruction {
  private final int result;

  private final int val1;

  private final int val2;

  private final IComparisonInstruction.Operator operator;

  /**
   */
  public SSAComparisonInstruction(int index, IComparisonInstruction.Operator operator, int result, int val1, int val2) {
    super(index);
    this.operator = operator;
    this.result = result;
    this.val1 = val1;
    this.val2 = val2;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length != 2) {
      throw new IllegalArgumentException("expected 2 uses or null, but got " + uses.length);
    }
    return insts.ComparisonInstruction(iindex, operator, defs == null || defs.length == 0 ? result : defs[0],
        uses == null ? val1 : uses[0], uses == null ? val2 : uses[1]);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = compare " + getValueString(symbolTable, val1) + ","
        + getValueString(symbolTable, val2) + " opcode=" + operator;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitComparison(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? val1 : val2;
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ 2371 * val1 + val2;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  /**
   * @return Returns the opcode.
   */
  public IComparisonInstruction.Operator getOperator() {
    return operator;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.IComparisonInstruction;

/**
 * SSA Instruction for comparisons between floats, longs and doubles
 */
public class SSAComparisonInstruction extends SSAInstruction {
  private final int result;

  private final int val1;

  private final int val2;

  private final IComparisonInstruction.Operator operator;

  /**
   */
  public SSAComparisonInstruction(IComparisonInstruction.Operator operator, int result, int val1, int val2) {
    super();
    this.operator = operator;
    this.result = result;
    this.val1 = val1;
    this.val2 = val2;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length != 2) {
      throw new IllegalArgumentException("expected 2 uses or null, but got " + uses.length);
    }
    return insts.ComparisonInstruction(operator, defs == null || defs.length == 0 ? result : defs[0],
        uses == null ? val1 : uses[0], uses == null ? val2 : uses[1]);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = compare " + getValueString(symbolTable, val1) + ","
        + getValueString(symbolTable, val2) + " opcode=" + operator;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitComparison(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? val1 : val2;
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ 2371 * val1 + val2;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  /**
   * @return Returns the opcode.
   */
  public IComparisonInstruction.Operator getOperator() {
    return operator;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.IComparisonInstruction;

/**
 * SSA Instruction for comparisons between floats, longs and doubles
 */
public class SSAComparisonInstruction extends SSAInstruction {
  private final int result;

  private final int val1;

  private final int val2;

  private final IComparisonInstruction.Operator operator;

  /**
   */
  public SSAComparisonInstruction(int index, IComparisonInstruction.Operator operator, int result, int val1, int val2) {
    super(index);
    this.operator = operator;
    this.result = result;
    this.val1 = val1;
    this.val2 = val2;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length != 2) {
      throw new IllegalArgumentException("expected 2 uses or null, but got " + uses.length);
    }
    return insts.ComparisonInstruction(iindex, operator, defs == null || defs.length == 0 ? result : defs[0],
        uses == null ? val1 : uses[0], uses == null ? val2 : uses[1]);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = compare " + getValueString(symbolTable, val1) + ","
        + getValueString(symbolTable, val2) + " opcode=" + operator;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitComparison(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? val1 : val2;
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ 2371 * val1 + val2;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  /**
   * @return Returns the opcode.
   */
  public IComparisonInstruction.Operator getOperator() {
    return operator;
  }
}
File
SSAComparisonInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction.IOperator;
import com.ibm.wala.types.TypeReference;

/**
 * A conditional branch instruction, which tests two values according to some {@link IOperator}.
 */
public class SSAConditionalBranchInstruction extends SSAInstruction {
  private final IConditionalBranchInstruction.IOperator operator;

  private final int val1;

  private final int val2;

  private final TypeReference type;

  public SSAConditionalBranchInstruction(int index, IConditionalBranchInstruction.IOperator operator, TypeReference type, int val1, int val2)
      throws IllegalArgumentException {
    super(index);
    this.operator = operator;
    this.val1 = val1;
    this.val2 = val2;
    this.type = type;
    if (val1 <= 0) {
      throw new IllegalArgumentException("Invalid val1: " + val1);
    }
    if (val2 <= 0) {
      throw new IllegalArgumentException("Invalid val2: " + val2);
    }
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length < 2) {
      throw new IllegalArgumentException("(uses != null) and (uses.length < 2)");
    }
    return insts.ConditionalBranchInstruction(iindex, operator, type, uses == null ? val1 : uses[0], uses == null ? val2 : uses[1]);
  }

  public IConditionalBranchInstruction.IOperator getOperator() {
    return operator;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "conditional branch(" + operator + ") " + getValueString(symbolTable, val1) + "," + getValueString(symbolTable, val2);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitConditionalBranch(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? val1 : val2;
  }

  public TypeReference getType() {
    return type;
  }

  public boolean isObjectComparison() {
    return type == TypeReference.JavaLangObject;
  }

  public boolean isIntegerComparison() {
    return type == TypeReference.Int;
  }

  @Override
  public int hashCode() {
    return 7151 * val1 + val2;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction.IOperator;
import com.ibm.wala.types.TypeReference;

/**
 * A conditional branch instruction, which tests two values according to some {@link IOperator}.
 */
public class SSAConditionalBranchInstruction extends SSAInstruction {
  private final IConditionalBranchInstruction.IOperator operator;

  private final int val1;

  private final int val2;

  private final TypeReference type;

  public SSAConditionalBranchInstruction(IConditionalBranchInstruction.IOperator operator, TypeReference type, int val1, int val2)
      throws IllegalArgumentException {
    super();
    this.operator = operator;
    this.val1 = val1;
    this.val2 = val2;
    this.type = type;
    if (val1 <= 0) {
      throw new IllegalArgumentException("Invalid val1: " + val1);
    }
    if (val2 <= 0) {
      throw new IllegalArgumentException("Invalid val2: " + val2);
    }
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length < 2) {
      throw new IllegalArgumentException("(uses != null) and (uses.length < 2)");
    }
    return insts.ConditionalBranchInstruction(operator, type, uses == null ? val1 : uses[0], uses == null ? val2 : uses[1]);
  }

  public IConditionalBranchInstruction.IOperator getOperator() {
    return operator;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "conditional branch(" + operator + ") " + getValueString(symbolTable, val1) + "," + getValueString(symbolTable, val2);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitConditionalBranch(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? val1 : val2;
  }

  public TypeReference getType() {
    return type;
  }

  public boolean isObjectComparison() {
    return type == TypeReference.JavaLangObject;
  }

  public boolean isIntegerComparison() {
    return type == TypeReference.Int;
  }

  @Override
  public int hashCode() {
    return 7151 * val1 + val2;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction.IOperator;
import com.ibm.wala.types.TypeReference;

/**
 * A conditional branch instruction, which tests two values according to some {@link IOperator}.
 */
public class SSAConditionalBranchInstruction extends SSAInstruction {
  private final IConditionalBranchInstruction.IOperator operator;

  private final int val1;

  private final int val2;

  private final TypeReference type;

  public SSAConditionalBranchInstruction(int index, IConditionalBranchInstruction.IOperator operator, TypeReference type, int val1, int val2)
      throws IllegalArgumentException {
    super(index);
    this.operator = operator;
    this.val1 = val1;
    this.val2 = val2;
    this.type = type;
    if (val1 <= 0) {
      throw new IllegalArgumentException("Invalid val1: " + val1);
    }
    if (val2 <= 0) {
      throw new IllegalArgumentException("Invalid val2: " + val2);
    }
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length < 2) {
      throw new IllegalArgumentException("(uses != null) and (uses.length < 2)");
    }
    return insts.ConditionalBranchInstruction(iindex, operator, type, uses == null ? val1 : uses[0], uses == null ? val2 : uses[1]);
  }

  public IConditionalBranchInstruction.IOperator getOperator() {
    return operator;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "conditional branch(" + operator + ") " + getValueString(symbolTable, val1) + "," + getValueString(symbolTable, val2);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitConditionalBranch(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return (j == 0) ? val1 : val2;
  }

  public TypeReference getType() {
    return type;
  }

  public boolean isObjectComparison() {
    return type == TypeReference.JavaLangObject;
  }

  public boolean isIntegerComparison() {
    return type == TypeReference.Int;
  }

  @Override
  public int hashCode() {
    return 7151 * val1 + val2;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
File
SSAConditionalBranchInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * An instruction which converts a value of one primitive type into another primitive type.
 */
public abstract class SSAConversionInstruction extends SSAInstruction {
  private final int result;

  private final int val;

  private final TypeReference fromType;

  private final TypeReference toType;

  protected SSAConversionInstruction(int index, int result, int val, TypeReference fromType, TypeReference toType) {
    super(index);
    this.result = result;
    this.val = val;
    this.fromType = fromType;
    this.toType = toType;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = conversion(" + toType.getName() + ") " + getValueString(symbolTable, val);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitConversion(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  public TypeReference getToType() {
    return toType;
  }

  public TypeReference getFromType() {
    return fromType;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return val;
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ val;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * An instruction which converts a value of one primitive type into another primitive type.
 */
public abstract class SSAConversionInstruction extends SSAInstruction {
  private final int result;

  private final int val;

  private final TypeReference fromType;

  private final TypeReference toType;

  protected SSAConversionInstruction(int result, int val, TypeReference fromType, TypeReference toType) {
    super();
    this.result = result;
    this.val = val;
    this.fromType = fromType;
    this.toType = toType;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = conversion(" + toType.getName() + ") " + getValueString(symbolTable, val);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitConversion(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  public TypeReference getToType() {
    return toType;
  }

  public TypeReference getFromType() {
    return fromType;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return val;
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ val;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * An instruction which converts a value of one primitive type into another primitive type.
 */
public abstract class SSAConversionInstruction extends SSAInstruction {
  private final int result;

  private final int val;

  private final TypeReference fromType;

  private final TypeReference toType;

  protected SSAConversionInstruction(int index, int result, int val, TypeReference fromType, TypeReference toType) {
    super(index);
    this.result = result;
    this.val = val;
    this.fromType = fromType;
    this.toType = toType;
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = conversion(" + toType.getName() + ") " + getValueString(symbolTable, val);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitConversion(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  public TypeReference getToType() {
    return toType;
  }

  public TypeReference getFromType() {
    return fromType;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return val;
  }

  @Override
  public int hashCode() {
    return 6311 * result ^ val;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }
}
File
SSAConversionInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
    return field;
  }

<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;

/**
 * Abstract base class for {@link SSAGetInstruction} and {@link SSAPutInstruction}.
 */
public abstract class SSAFieldAccessInstruction extends SSAInstruction {

  private final FieldReference field;

  private final int ref;

  protected SSAFieldAccessInstruction(int index, FieldReference field, int ref) throws IllegalArgumentException {
    super(index);
    this.field = field;
    this.ref = ref;
    if (field == null) {
      throw new IllegalArgumentException("field cannot be null");
    }
  }

  public TypeReference getDeclaredFieldType() {
    return field.getFieldType();
  }

  public FieldReference getDeclaredField() {
  public int getRef() {
    return ref;
  }

  public boolean isStatic() {
    return ref == -1;
  }

  @Override
  public boolean isPEI() {
    return !isStatic();
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;

/**
 * Abstract base class for {@link SSAGetInstruction} and {@link SSAPutInstruction}.
 */
public abstract class SSAFieldAccessInstruction extends SSAInstruction {

  private final FieldReference field;

  private final int ref;

  protected SSAFieldAccessInstruction(FieldReference field, int ref) throws IllegalArgumentException {
    super();
    this.field = field;
    this.ref = ref;
    if (field == null) {
      throw new IllegalArgumentException("field cannot be null");
    }
  }

  public TypeReference getDeclaredFieldType() {
    return field.getFieldType();
  }

  public FieldReference getDeclaredField() {
    return field;
  }

  public int getRef() {
    return ref;
  }

  public boolean isStatic() {
    return ref == -1;
  }

  @Override
  public boolean isPEI() {
    return !isStatic();
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
    this.ref = ref;
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;

/**
 * Abstract base class for {@link SSAGetInstruction} and {@link SSAPutInstruction}.
 */
public abstract class SSAFieldAccessInstruction extends SSAInstruction {

  private final FieldReference field;

  private final int ref;

  protected SSAFieldAccessInstruction(int index, FieldReference field, int ref) throws IllegalArgumentException {
    super(index);
    this.field = field;
    if (field == null) {
      throw new IllegalArgumentException("field cannot be null");
    }
  }

  public TypeReference getDeclaredFieldType() {
    return field.getFieldType();
  }

  public FieldReference getDeclaredField() {
    return field;
  }

  public int getRef() {
    return ref;
  }

  public boolean isStatic() {
    return ref == -1;
  }

  @Override
  public boolean isPEI() {
    return !isStatic();
  }
}
File
SSAFieldAccessInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * A "catch" instruction, inserted at the head of a catch block, which assigns a pending exception object to a local variable.
 * 
 * In SSA {@link IR}s, these instructions do not appear in the normal instruction array returned by IR.getInstructions();
 * instead these instructions live in {@link ISSABasicBlock}.
 */
public class SSAGetCaughtExceptionInstruction extends SSAInstruction {
  private final int exceptionValueNumber;

  private final int bbNumber;

  public SSAGetCaughtExceptionInstruction(int index, int bbNumber, int exceptionValueNumber) {
    super(index);
    this.exceptionValueNumber = exceptionValueNumber;
    this.bbNumber = bbNumber;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    assert defs == null || defs.length == 1;
    return insts.GetCaughtExceptionInstruction(iindex, bbNumber, defs == null ? exceptionValueNumber : defs[0]);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    StringBuffer s = new StringBuffer();
    s.append(getValueString(symbolTable, exceptionValueNumber)).append(" = getCaughtException ");
    return s.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitGetCaughtException(this);
  }

  /**
   * Returns the result.
   * 
   * @return int
   */
  public int getException() {
    return exceptionValueNumber;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return exceptionValueNumber;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return exceptionValueNumber;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  public int getBasicBlockNumber() {
    return bbNumber;
  }

  @Override
  public int hashCode() {
    return 2243 * exceptionValueNumber;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * A "catch" instruction, inserted at the head of a catch block, which assigns a pending exception object to a local variable.
 * 
 * In SSA {@link IR}s, these instructions do not appear in the normal instruction array returned by IR.getInstructions();
 * instead these instructions live in {@link ISSABasicBlock}.
 */
public class SSAGetCaughtExceptionInstruction extends SSAInstruction {
  private final int exceptionValueNumber;

  private final int bbNumber;

  public SSAGetCaughtExceptionInstruction(int bbNumber, int exceptionValueNumber) {
    super();
    this.exceptionValueNumber = exceptionValueNumber;
    this.bbNumber = bbNumber;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    assert defs == null || defs.length == 1;
    return insts.GetCaughtExceptionInstruction(bbNumber, defs == null ? exceptionValueNumber : defs[0]);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    StringBuffer s = new StringBuffer();
    s.append(getValueString(symbolTable, exceptionValueNumber)).append(" = getCaughtException ");
    return s.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitGetCaughtException(this);
  }

  /**
   * Returns the result.
   * 
   * @return int
   */
  public int getException() {
    return exceptionValueNumber;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return exceptionValueNumber;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return exceptionValueNumber;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  public int getBasicBlockNumber() {
    return bbNumber;
  }

  @Override
  public int hashCode() {
    return 2243 * exceptionValueNumber;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * A "catch" instruction, inserted at the head of a catch block, which assigns a pending exception object to a local variable.
 * 
 * In SSA {@link IR}s, these instructions do not appear in the normal instruction array returned by IR.getInstructions();
 * instead these instructions live in {@link ISSABasicBlock}.
 */
public class SSAGetCaughtExceptionInstruction extends SSAInstruction {
  private final int exceptionValueNumber;

  private final int bbNumber;

  public SSAGetCaughtExceptionInstruction(int index, int bbNumber, int exceptionValueNumber) {
    super(index);
    this.exceptionValueNumber = exceptionValueNumber;
    this.bbNumber = bbNumber;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    assert defs == null || defs.length == 1;
    return insts.GetCaughtExceptionInstruction(iindex, bbNumber, defs == null ? exceptionValueNumber : defs[0]);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    StringBuffer s = new StringBuffer();
    s.append(getValueString(symbolTable, exceptionValueNumber)).append(" = getCaughtException ");
    return s.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitGetCaughtException(this);
  }

  /**
   * Returns the result.
   * 
   * @return int
   */
  public int getException() {
    return exceptionValueNumber;
  }
  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return exceptionValueNumber;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return exceptionValueNumber;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  public int getBasicBlockNumber() {
    return bbNumber;
  }

  @Override
  public int hashCode() {
    return 2243 * exceptionValueNumber;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
File
SSAGetCaughtExceptionInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.FieldReference;

/**
 * SSA instruction that reads a field (i.e. getstatic or getfield).
 */
public abstract class SSAGetInstruction extends SSAFieldAccessInstruction {
  private final int result;

  protected SSAGetInstruction(int index, int result, int ref, FieldReference field) {
    super(index, field, ref);
    this.result = result;
  }

  protected SSAGetInstruction(int index, int result, FieldReference field) {
    super(index, field, -1);
    this.result = result;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (isStatic())
      return insts.GetInstruction(iindex, defs == null || defs.length == 0 ? result : defs[0], getDeclaredField());
    else
      return insts.GetInstruction(iindex, defs == null || defs.length == 0 ? result : defs[0], uses == null ? getRef() : uses[0],
          getDeclaredField());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    if (isStatic()) {
      return getValueString(symbolTable, result) + " = getstatic " + getDeclaredField();
    } else {
      return getValueString(symbolTable, result) + " = getfield " + getDeclaredField() + " "
          + getValueString(symbolTable, getRef());
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitGet(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return (isStatic()) ? 0 : 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0 && getRef() != -1;
    return getRef();
  }

  @Override
  public int hashCode() {
    return result * 2371 + 6521;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.FieldReference;

/**
 * SSA instruction that reads a field (i.e. getstatic or getfield).
 */
public abstract class SSAGetInstruction extends SSAFieldAccessInstruction {
  private final int result;

  protected SSAGetInstruction(int result, int ref, FieldReference field) {
    super(field, ref);
    this.result = result;
  }

  protected SSAGetInstruction(int result, FieldReference field) {
    super(field, -1);
    this.result = result;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (isStatic())
      return insts.GetInstruction(defs == null || defs.length == 0 ? result : defs[0], getDeclaredField());
    else
      return insts.GetInstruction(defs == null || defs.length == 0 ? result : defs[0], uses == null ? getRef() : uses[0],
          getDeclaredField());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    if (isStatic()) {
      return getValueString(symbolTable, result) + " = getstatic " + getDeclaredField();
    } else {
      return getValueString(symbolTable, result) + " = getfield " + getDeclaredField() + " "
          + getValueString(symbolTable, getRef());
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitGet(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return (isStatic()) ? 0 : 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0 && getRef() != -1;
    return getRef();
  }

  @Override
  public int hashCode() {
    return result * 2371 + 6521;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.FieldReference;

/**
 * SSA instruction that reads a field (i.e. getstatic or getfield).
 */
public abstract class SSAGetInstruction extends SSAFieldAccessInstruction {
  private final int result;

  protected SSAGetInstruction(int index, int result, int ref, FieldReference field) {
    super(index, field, ref);
    this.result = result;
  }

  protected SSAGetInstruction(int index, int result, FieldReference field) {
    super(index, field, -1);
    this.result = result;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (isStatic())
      return insts.GetInstruction(iindex, defs == null || defs.length == 0 ? result : defs[0], getDeclaredField());
    else
      return insts.GetInstruction(iindex, defs == null || defs.length == 0 ? result : defs[0], uses == null ? getRef() : uses[0],
          getDeclaredField());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    if (isStatic()) {
      return getValueString(symbolTable, result) + " = getstatic " + getDeclaredField();
    } else {
      return getValueString(symbolTable, result) + " = getfield " + getDeclaredField() + " "
          + getValueString(symbolTable, getRef());
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitGet(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return (isStatic()) ? 0 : 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0 && getRef() != -1;
    return getRef();
  }

  @Override
  public int hashCode() {
    return result * 2371 + 6521;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }
}
File
SSAGetInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * Unconditional branch instruction for SSA form.
 */
public class SSAGotoInstruction extends SSAInstruction {

  public SSAGotoInstruction(int index) {
    super(index);
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    return insts.GotoInstruction(iindex);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "goto";
  }

  /**
   * @throws IllegalArgumentException
   *           if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitGoto(this);
  }

  @Override
  public int hashCode() {
    return 1409; // XXX weak!
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return false;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * Unconditional branch instruction for SSA form.
 */
public class SSAGotoInstruction extends SSAInstruction {

  public SSAGotoInstruction() {
    super();
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    return insts.GotoInstruction();
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "goto";
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException
   *           if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitGoto(this);
  }

  @Override
  public int hashCode() {
    return 1409; // XXX weak!
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return false;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * Unconditional branch instruction for SSA form.
 */
public class SSAGotoInstruction extends SSAInstruction {

  public SSAGotoInstruction(int index) {
    super(index);
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    return insts.GotoInstruction(iindex);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "goto";
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException
   *           if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitGoto(this);
  }

  @Override
  public int hashCode() {
    return 1409; // XXX weak!
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return false;
  }
}
File
SSAGotoInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Package declaration
Chunk
Conflicting content
    super(index);
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * A dynamic type test (instanceof) instruction.
 */
public class SSAInstanceofInstruction extends SSAInstruction {
  private final int result;

  private final int ref;

  private final TypeReference checkedType;

  public SSAInstanceofInstruction(int index, int result, int ref, TypeReference checkedType) {
    this.result = result;
    this.ref = ref;
    this.checkedType = checkedType;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("defs.length == 0");
    }
    if (uses != null && uses.length == 0) {
      throw new IllegalArgumentException("uses.length == 0");
    }
    return insts.InstanceofInstruction(iindex, defs == null || defs.length == 0 ? result : defs[0], uses == null ? ref : uses[0],
        checkedType);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = instanceof " + getValueString(symbolTable, ref) + " " + checkedType;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitInstanceof(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  public TypeReference getCheckedType() {
    return checkedType;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return ref;
  }

  @Override
  public int hashCode() {
    return ref * 677 ^ result * 3803;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  public int getRef() {
    return ref;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * A dynamic type test (instanceof) instruction.
 */
public class SSAInstanceofInstruction extends SSAInstruction {
  private final int result;

  private final int ref;

  private final TypeReference checkedType;

  public SSAInstanceofInstruction(int result, int ref, TypeReference checkedType) {
    super();
    this.result = result;
    this.ref = ref;
    this.checkedType = checkedType;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("defs.length == 0");
    }
    if (uses != null && uses.length == 0) {
      throw new IllegalArgumentException("uses.length == 0");
    }
    return insts.InstanceofInstruction(defs == null || defs.length == 0 ? result : defs[0], uses == null ? ref : uses[0],
        checkedType);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = instanceof " + getValueString(symbolTable, ref) + " " + checkedType;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitInstanceof(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  public TypeReference getCheckedType() {
    return checkedType;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return ref;
  }

  @Override
  public int hashCode() {
    return ref * 677 ^ result * 3803;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  public int getRef() {
    return ref;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * A dynamic type test (instanceof) instruction.
 */
public class SSAInstanceofInstruction extends SSAInstruction {
  private final int result;

  private final int ref;

  private final TypeReference checkedType;

  public SSAInstanceofInstruction(int index, int result, int ref, TypeReference checkedType) {
    super(index);
    this.result = result;
    this.ref = ref;
    this.checkedType = checkedType;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("defs.length == 0");
    }
    if (uses != null && uses.length == 0) {
      throw new IllegalArgumentException("uses.length == 0");
    }
    return insts.InstanceofInstruction(iindex, defs == null || defs.length == 0 ? result : defs[0], uses == null ? ref : uses[0],
        checkedType);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = instanceof " + getValueString(symbolTable, ref) + " " + checkedType;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitInstanceof(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  public TypeReference getCheckedType() {
    return checkedType;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return ref;
  }

  @Override
  public int hashCode() {
    return ref * 677 ^ result * 3803;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  public int getRef() {
    return ref;
  }
}
File
SSAInstanceofInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import java.util.Collection;
import java.util.Collections;

import com.ibm.wala.types.TypeReference;

/**
 * An instruction in SSA form.
 */
public abstract class SSAInstruction {

  public static final int NO_INDEX = -1;
  
  public final int iindex;
  
  /**
   * prevent instantiation by the outside
   */
  protected SSAInstruction(int iindex) {
    this.iindex = iindex;
  }

  /**
   * This method is meant to be used during SSA conversion for an IR that is not in SSA form. It creates a new SSAInstruction of the
   * same type as the receiver, with a combination of the receiver's uses and defs and those from the method parameters.
   * 
   * In particular, if the 'defs' parameter is null, then the new instruction has the same defs as the receiver. If 'defs' is not
   * null, it must be an array with a size equal to the number of defs that the receiver instruction has. In this case, the new
   * instruction has defs taken from the array. The uses of the new instruction work in the same way with the 'uses' parameter.
   * 
   * Note that this only applies to CAst-based IR translation, since Java bytecode-based IR generation uses a different SSA
   * construction mechanism.
   * 
   * TODO: move this into the SSAInstructionFactory
   */
  public abstract SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses);

  public abstract String toString(SymbolTable symbolTable);

  @Override
  public String toString() {
    return toString(null);
  }

  protected String getValueString(SymbolTable symbolTable, int valueNumber) {
    if (symbolTable == null) {
      return Integer.toString(valueNumber);
    } else {
      return symbolTable.getValueString(valueNumber);
    }
  }

  /**
   * Apply an IVisitor to this instruction. We invoke the appropriate IVisitor method according to the type of this instruction.
   */
  public abstract void visit(IVisitor v);

  /**
   * This interface is used by Instruction.visit to dispatch based on the instruction type.
   */
  public static interface IVisitor {
    void visitGoto(SSAGotoInstruction instruction);

    void visitArrayLoad(SSAArrayLoadInstruction instruction);

    void visitArrayStore(SSAArrayStoreInstruction instruction);

    void visitBinaryOp(SSABinaryOpInstruction instruction);

    void visitUnaryOp(SSAUnaryOpInstruction instruction);

    void visitConversion(SSAConversionInstruction instruction);

    void visitComparison(SSAComparisonInstruction instruction);

    void visitConditionalBranch(SSAConditionalBranchInstruction instruction);

    void visitSwitch(SSASwitchInstruction instruction);

    void visitReturn(SSAReturnInstruction instruction);

    void visitGet(SSAGetInstruction instruction);

    void visitPut(SSAPutInstruction instruction);

    void visitInvoke(SSAInvokeInstruction instruction);

    void visitNew(SSANewInstruction instruction);

    void visitArrayLength(SSAArrayLengthInstruction instruction);

    void visitThrow(SSAThrowInstruction instruction);

    void visitMonitor(SSAMonitorInstruction instruction);

    void visitCheckCast(SSACheckCastInstruction instruction);

    void visitInstanceof(SSAInstanceofInstruction instruction);

    void visitPhi(SSAPhiInstruction instruction);

    void visitPi(SSAPiInstruction instruction);


    void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction);

    void visitLoadMetadata(SSALoadMetadataInstruction instruction);
  }

  /**
   * A base visitor implementation that does nothing.
   */
  public static abstract class Visitor implements IVisitor {
    public void visitGoto(SSAGotoInstruction instruction) {
    }

    public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
    }

    public void visitArrayStore(SSAArrayStoreInstruction instruction) {
    }

    public void visitBinaryOp(SSABinaryOpInstruction instruction) {
    }

    public void visitUnaryOp(SSAUnaryOpInstruction instruction) {
    }

    public void visitConversion(SSAConversionInstruction instruction) {
    }

    public void visitComparison(SSAComparisonInstruction instruction) {
    }

    public void visitConditionalBranch(SSAConditionalBranchInstruction instruction) {
    }

    public void visitSwitch(SSASwitchInstruction instruction) {
    }

    public void visitReturn(SSAReturnInstruction instruction) {
    }

    public void visitGet(SSAGetInstruction instruction) {
    }

    public void visitPut(SSAPutInstruction instruction) {
    }

    public void visitInvoke(SSAInvokeInstruction instruction) {
    }

    public void visitNew(SSANewInstruction instruction) {
    }

    public void visitArrayLength(SSAArrayLengthInstruction instruction) {
    }

    public void visitThrow(SSAThrowInstruction instruction) {
    }

    public void visitMonitor(SSAMonitorInstruction instruction) {
    }

    public void visitCheckCast(SSACheckCastInstruction instruction) {
    }

    public void visitInstanceof(SSAInstanceofInstruction instruction) {
    }

   */
    public void visitPhi(SSAPhiInstruction instruction) {
    }

    public void visitPi(SSAPiInstruction instruction) {
    }

    public void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction) {
    }

    public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
    }
  }

  /**
   * Does this instruction define a normal value, as distinct from a set of exceptions possibly thrown by it (e.g. for invoke
   * instructions).
   * 
   * @return true if the instruction does define a proper value.
   */
  public boolean hasDef() {
    return false;
  }

  public int getDef() {
    return -1;
  }

  /**
   * Return the ith def
   * 
   * @param i number of the def, starting at 0.
   */
  public int getDef(int i) {
    return -1;
  }

  public int getNumberOfDefs() {
    return 0;
  }

  public int getNumberOfUses() {
    return 0;
  }

  /**
   * @return value number representing the jth use in this instruction. -1 means TOP (i.e., the value doesn't matter)
   */
  public int getUse(int j) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  @Override
  public abstract int hashCode();

  /**
   * @return true iff this instruction may throw an exception.
   */
  public boolean isPEI() {
    return false;
  }

  /**
   * This method should never return null.
   * 
   * @return the set of exception types that an instruction might throw ... disregarding athrows and invokes.
   */
  public Collection getExceptionTypes() {
    assert !isPEI();
    return Collections.emptySet();
  }

  /**
   * @return true iff this instruction may fall through to the next
   */
  public abstract boolean isFallThrough();

  /**
   * We assume these instructions are canonical and managed by a governing IR object. Be careful.
   * 
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public final boolean equals(Object obj) {
    return this == obj;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import java.util.Collection;
import java.util.Collections;

import com.ibm.wala.types.TypeReference;

/**
 * An instruction in SSA form.
 */
public abstract class SSAInstruction {

  /**
   * prevent instantiation by the outside
  protected SSAInstruction() {
  }

  /**
   * This method is meant to be used during SSA conversion for an IR that is not in SSA form. It creates a new SSAInstruction of the
   * same type as the receiver, with a combination of the receiver's uses and defs and those from the method parameters.
   * 
   * In particular, if the 'defs' parameter is null, then the new instruction has the same defs as the receiver. If 'defs' is not
   * null, it must be an array with a size equal to the number of defs that the receiver instruction has. In this case, the new
   * instruction has defs taken from the array. The uses of the new instruction work in the same way with the 'uses' parameter.
   * 
   * Note that this only applies to CAst-based IR translation, since Java bytecode-based IR generation uses a different SSA
   * construction mechanism.
   * 
   * TODO: move this into the SSAInstructionFactory
   */
  public abstract SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses);

  public abstract String toString(SymbolTable symbolTable);

  @Override
  public String toString() {
    return toString(null);
  }

  protected String getValueString(SymbolTable symbolTable, int valueNumber) {
    if (symbolTable == null) {
      return Integer.toString(valueNumber);
    } else {
      return symbolTable.getValueString(valueNumber);
    }
  }

  /**
   * Apply an IVisitor to this instruction. We invoke the appropriate IVisitor method according to the type of this instruction.
   */
  public abstract void visit(IVisitor v);

  /**
   * This interface is used by Instruction.visit to dispatch based on the instruction type.
   */
  public static interface IVisitor {
    void visitGoto(SSAGotoInstruction instruction);

    void visitArrayLoad(SSAArrayLoadInstruction instruction);

    void visitArrayStore(SSAArrayStoreInstruction instruction);

    void visitBinaryOp(SSABinaryOpInstruction instruction);

    void visitUnaryOp(SSAUnaryOpInstruction instruction);

    void visitConversion(SSAConversionInstruction instruction);

    void visitComparison(SSAComparisonInstruction instruction);

    void visitConditionalBranch(SSAConditionalBranchInstruction instruction);

    void visitSwitch(SSASwitchInstruction instruction);

    void visitReturn(SSAReturnInstruction instruction);

    void visitGet(SSAGetInstruction instruction);

    void visitPut(SSAPutInstruction instruction);

    void visitInvoke(SSAInvokeInstruction instruction);

    void visitNew(SSANewInstruction instruction);

    void visitArrayLength(SSAArrayLengthInstruction instruction);

    void visitThrow(SSAThrowInstruction instruction);

    void visitMonitor(SSAMonitorInstruction instruction);

    void visitCheckCast(SSACheckCastInstruction instruction);

    void visitInstanceof(SSAInstanceofInstruction instruction);

    void visitPhi(SSAPhiInstruction instruction);

    void visitPi(SSAPiInstruction instruction);

    void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction);

    void visitLoadMetadata(SSALoadMetadataInstruction instruction);
  }

  /**
   * A base visitor implementation that does nothing.
   */
  public static abstract class Visitor implements IVisitor {
    public void visitGoto(SSAGotoInstruction instruction) {
    }

    public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
    }

    public void visitArrayStore(SSAArrayStoreInstruction instruction) {
    }

    public void visitBinaryOp(SSABinaryOpInstruction instruction) {
    }

    public void visitUnaryOp(SSAUnaryOpInstruction instruction) {
    }

    public void visitConversion(SSAConversionInstruction instruction) {
    }

    public void visitComparison(SSAComparisonInstruction instruction) {
    }

    public void visitConditionalBranch(SSAConditionalBranchInstruction instruction) {
    }

    public void visitSwitch(SSASwitchInstruction instruction) {
    }

    public void visitReturn(SSAReturnInstruction instruction) {
    }

    public void visitGet(SSAGetInstruction instruction) {
    }

    public void visitPut(SSAPutInstruction instruction) {
    }

    public void visitInvoke(SSAInvokeInstruction instruction) {
    }

    public void visitNew(SSANewInstruction instruction) {
    }

    public void visitArrayLength(SSAArrayLengthInstruction instruction) {
    }

    public void visitThrow(SSAThrowInstruction instruction) {
    }

    public void visitMonitor(SSAMonitorInstruction instruction) {
    }
    public void visitCheckCast(SSACheckCastInstruction instruction) {
    }

    public void visitInstanceof(SSAInstanceofInstruction instruction) {
    }

    public void visitPhi(SSAPhiInstruction instruction) {
    }

    public void visitPi(SSAPiInstruction instruction) {
    }

    public void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction) {
    }

    public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
    }
  }

  /**
   * Does this instruction define a normal value, as distinct from a set of exceptions possibly thrown by it (e.g. for invoke
   * instructions).
   * 
   * @return true if the instruction does define a proper value.
   */
  public boolean hasDef() {
    return false;
  }

  public int getDef() {
    return -1;
  }

  /**
   * Return the ith def
   * 
   * @param i number of the def, starting at 0.
   */
  public int getDef(int i) {
    return -1;
  }

  public int getNumberOfDefs() {
    return 0;
  }

  public int getNumberOfUses() {
    return 0;
  }

  /**
   * @return value number representing the jth use in this instruction. -1 means TOP (i.e., the value doesn't matter)
   */
  public int getUse(int j) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  @Override
  public abstract int hashCode();

  /**
   * @return true iff this instruction may throw an exception.
   */
  public boolean isPEI() {
    return false;
  }

  /**
   * This method should never return null.
   * 
   * @return the set of exception types that an instruction might throw ... disregarding athrows and invokes.
   */
  public Collection getExceptionTypes() {
    assert !isPEI();
    return Collections.emptySet();
  }

  /**
   * @return true iff this instruction may fall through to the next
   */
  public abstract boolean isFallThrough();

  /**
   * We assume these instructions are canonical and managed by a governing IR object. Be careful.
   * 
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public final boolean equals(Object obj) {
    return this == obj;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
 *
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import java.util.Collection;
import java.util.Collections;

import com.ibm.wala.types.TypeReference;

/**
 * An instruction in SSA form.
 */
public abstract class SSAInstruction {

  public static final int NO_INDEX = -1;
  
  public final int iindex;
  
  /**
   * prevent instantiation by the outside
   */
  protected SSAInstruction(int iindex) {
    this.iindex = iindex;
  }

  /**
   * This method is meant to be used during SSA conversion for an IR that is not in SSA form. It creates a new SSAInstruction of the
   * same type as the receiver, with a combination of the receiver's uses and defs and those from the method parameters.
   * 
   * In particular, if the 'defs' parameter is null, then the new instruction has the same defs as the receiver. If 'defs' is not
   * null, it must be an array with a size equal to the number of defs that the receiver instruction has. In this case, the new
   * instruction has defs taken from the array. The uses of the new instruction work in the same way with the 'uses' parameter.
   * 
   * Note that this only applies to CAst-based IR translation, since Java bytecode-based IR generation uses a different SSA
   * construction mechanism.
   * 
   * TODO: move this into the SSAInstructionFactory
   */
  public abstract SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses);

  public abstract String toString(SymbolTable symbolTable);

  @Override
  public String toString() {
    return toString(null);
  }

  protected String getValueString(SymbolTable symbolTable, int valueNumber) {
    if (symbolTable == null) {
      return Integer.toString(valueNumber);
    } else {
      return symbolTable.getValueString(valueNumber);
    }
  }

  /**
   * Apply an IVisitor to this instruction. We invoke the appropriate IVisitor method according to the type of this instruction.
   */
  public abstract void visit(IVisitor v);

  /**
   * This interface is used by Instruction.visit to dispatch based on the instruction type.
   */
  public static interface IVisitor {
    void visitGoto(SSAGotoInstruction instruction);

    void visitArrayLoad(SSAArrayLoadInstruction instruction);

    void visitArrayStore(SSAArrayStoreInstruction instruction);

    void visitBinaryOp(SSABinaryOpInstruction instruction);

    void visitUnaryOp(SSAUnaryOpInstruction instruction);

    void visitConversion(SSAConversionInstruction instruction);

    void visitComparison(SSAComparisonInstruction instruction);

    void visitConditionalBranch(SSAConditionalBranchInstruction instruction);

    void visitSwitch(SSASwitchInstruction instruction);

    void visitReturn(SSAReturnInstruction instruction);

    void visitGet(SSAGetInstruction instruction);

    void visitPut(SSAPutInstruction instruction);

    void visitInvoke(SSAInvokeInstruction instruction);

    void visitNew(SSANewInstruction instruction);

    void visitArrayLength(SSAArrayLengthInstruction instruction);

    void visitThrow(SSAThrowInstruction instruction);

    void visitMonitor(SSAMonitorInstruction instruction);

    void visitCheckCast(SSACheckCastInstruction instruction);

    void visitInstanceof(SSAInstanceofInstruction instruction);

    void visitPhi(SSAPhiInstruction instruction);

    void visitPi(SSAPiInstruction instruction);

    void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction);

    void visitLoadMetadata(SSALoadMetadataInstruction instruction);
  }

  /**
   * A base visitor implementation that does nothing.
   */
  public static abstract class Visitor implements IVisitor {
    public void visitGoto(SSAGotoInstruction instruction) {
    }

    public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
    }

    public void visitArrayStore(SSAArrayStoreInstruction instruction) {
    }

    public void visitBinaryOp(SSABinaryOpInstruction instruction) {
    }

    public void visitUnaryOp(SSAUnaryOpInstruction instruction) {
    }

    public void visitConversion(SSAConversionInstruction instruction) {
    }

    public void visitComparison(SSAComparisonInstruction instruction) {
    }

    public void visitConditionalBranch(SSAConditionalBranchInstruction instruction) {
    }

    public void visitSwitch(SSASwitchInstruction instruction) {
    }

    public void visitReturn(SSAReturnInstruction instruction) {
    }

    public void visitGet(SSAGetInstruction instruction) {
    }

    public void visitPut(SSAPutInstruction instruction) {
    }

    public void visitInvoke(SSAInvokeInstruction instruction) {
    }

    public void visitNew(SSANewInstruction instruction) {
    }

    public void visitArrayLength(SSAArrayLengthInstruction instruction) {
    }

    public void visitThrow(SSAThrowInstruction instruction) {
    }

    public void visitMonitor(SSAMonitorInstruction instruction) {
    }

    public void visitCheckCast(SSACheckCastInstruction instruction) {
    }

    public void visitInstanceof(SSAInstanceofInstruction instruction) {
    }

    public void visitPhi(SSAPhiInstruction instruction) {
    }

    public void visitPi(SSAPiInstruction instruction) {
    }

    public void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction) {
    }

    public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
    }
  }

  /**
   * Does this instruction define a normal value, as distinct from a set of exceptions possibly thrown by it (e.g. for invoke
   * instructions).
   * 
   * @return true if the instruction does define a proper value.
   */
  public boolean hasDef() {
    return false;
  }

  public int getDef() {
    return -1;
  }

  /**
   * Return the ith def
   * 
   * @param i number of the def, starting at 0.
   */
  public int getDef(int i) {
    return -1;
  }

  public int getNumberOfDefs() {
    return 0;
  }

  public int getNumberOfUses() {
    return 0;
  }

  /**
   * @return value number representing the jth use in this instruction. -1 means TOP (i.e., the value doesn't matter)
   */
  public int getUse(int j) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  @Override
  public abstract int hashCode();

  /**
   * @return true iff this instruction may throw an exception.
   */
  public boolean isPEI() {
    return false;
  }

  /**
   * This method should never return null.
   * 
   * @return the set of exception types that an instruction might throw ... disregarding athrows and invokes.
   */
  public Collection getExceptionTypes() {
    assert !isPEI();
    return Collections.emptySet();
  }

  /**
   * @return true iff this instruction may fall through to the next
   */
  public abstract boolean isFallThrough();

  /**
   * We assume these instructions are canonical and managed by a governing IR object. Be careful.
   * 
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public final boolean equals(Object obj) {
    return this == obj;
  }
}
File
SSAInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
  SSAGotoInstruction GotoInstruction(int iindex);
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IComparisonInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;

/**
 * An instruction factory for SSA. 
 */
public interface SSAInstructionFactory {

  SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, TypeReference pointeeType);

  SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, int indexVal, TypeReference pointeeType);

  SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, FieldReference field, TypeReference pointeeType);
  
  SSAArrayLengthInstruction ArrayLengthInstruction(int iindex, int result, int arrayref);

  SSAArrayLoadInstruction ArrayLoadInstruction(int iindex, int result, int arrayref, int index, TypeReference declaredType);

  SSAArrayStoreInstruction ArrayStoreInstruction(int iindex, int arrayref, int index, int value, TypeReference declaredType);

  SSABinaryOpInstruction BinaryOpInstruction(int iindex, IBinaryOpInstruction.IOperator operator, boolean overflow, boolean unsigned,
      int result, int val1, int val2, boolean mayBeInteger);

  SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, int[] typeValues, boolean isPEI);

  SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, int typeValue, boolean isPEI);

  SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, TypeReference[] types, boolean isPEI);

  SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, TypeReference type, boolean isPEI);

  SSAComparisonInstruction ComparisonInstruction(int iindex, IComparisonInstruction.Operator operator, int result, int val1, int val2);

  SSAConditionalBranchInstruction ConditionalBranchInstruction(int iindex, IConditionalBranchInstruction.IOperator operator,
      TypeReference type, int val1, int val2);

  SSAConversionInstruction ConversionInstruction(int iindex, int result, int val, TypeReference fromType, TypeReference toType, boolean overflow);

  SSAGetCaughtExceptionInstruction GetCaughtExceptionInstruction(int iindex, int bbNumber, int exceptionValueNumber);

  SSAGetInstruction GetInstruction(int iindex, int result, FieldReference field);

  SSAGetInstruction GetInstruction(int iindex, int result, int ref, FieldReference field);


  SSAInstanceofInstruction InstanceofInstruction(int iindex, int result, int ref, TypeReference checkedType);

  SSAInvokeInstruction InvokeInstruction(int iindex, int result, int[] params, int exception, CallSiteReference site);

  SSAInvokeInstruction InvokeInstruction(int iindex, int[] params, int exception, CallSiteReference site);

  SSALoadIndirectInstruction LoadIndirectInstruction(int iindex, int lval, TypeReference t, int addressVal);
  
  SSALoadMetadataInstruction LoadMetadataInstruction(int iindex, int lval, TypeReference entityType, Object token);

  SSAMonitorInstruction MonitorInstruction(int iindex, int ref, boolean isEnter);

  SSANewInstruction NewInstruction(int iindex, int result, NewSiteReference site);

  SSANewInstruction NewInstruction(int iindex, int result, NewSiteReference site, int[] params);

  SSAPhiInstruction PhiInstruction(int iindex, int result, int[] params);

  SSAPiInstruction PiInstruction(int iindex, int result, int val, int piBlock, int successorBlock, SSAInstruction cause);

  SSAPutInstruction PutInstruction(int iindex, int ref, int value, FieldReference field);

  SSAPutInstruction PutInstruction(int iindex, int value, FieldReference field);

  SSAReturnInstruction ReturnInstruction(int iindex);

  SSAReturnInstruction ReturnInstruction(int iindex, int result, boolean isPrimitive);

  SSAStoreIndirectInstruction StoreIndirectInstruction(int iindex, int addressVal, int rval, TypeReference pointeeType);
  
  SSASwitchInstruction SwitchInstruction(int iindex, int val, int defaultLabel, int[] casesAndLabels);

  SSAThrowInstruction ThrowInstruction(int iindex, int exception);

  SSAUnaryOpInstruction UnaryOpInstruction(int iindex, IUnaryOpInstruction.IOperator operator, int result, int val);

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IComparisonInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;

/**
 * An instruction factory for SSA. 
 */
public interface SSAInstructionFactory {

  SSAAddressOfInstruction AddressOfInstruction(int lval, int local, TypeReference pointeeType);

  SSAAddressOfInstruction AddressOfInstruction(int lval, int local, int indexVal, TypeReference pointeeType);

  SSAAddressOfInstruction AddressOfInstruction(int lval, int local, FieldReference field, TypeReference pointeeType);
  
  SSAArrayLengthInstruction ArrayLengthInstruction(int result, int arrayref);

  SSAArrayLoadInstruction ArrayLoadInstruction(int result, int arrayref, int index, TypeReference declaredType);

  SSAArrayStoreInstruction ArrayStoreInstruction(int arrayref, int index, int value, TypeReference declaredType);

  SSABinaryOpInstruction BinaryOpInstruction(IBinaryOpInstruction.IOperator operator, boolean overflow, boolean unsigned,
      int result, int val1, int val2, boolean mayBeInteger);

  SSACheckCastInstruction CheckCastInstruction(int result, int val, int[] typeValues, boolean isPEI);

  SSACheckCastInstruction CheckCastInstruction(int result, int val, int typeValue, boolean isPEI);

  SSACheckCastInstruction CheckCastInstruction(int result, int val, TypeReference[] types, boolean isPEI);

  SSACheckCastInstruction CheckCastInstruction(int result, int val, TypeReference type, boolean isPEI);

  SSAComparisonInstruction ComparisonInstruction(IComparisonInstruction.Operator operator, int result, int val1, int val2);

  SSAConditionalBranchInstruction ConditionalBranchInstruction(IConditionalBranchInstruction.IOperator operator,
      TypeReference type, int val1, int val2);

  SSAConversionInstruction ConversionInstruction(int result, int val, TypeReference fromType, TypeReference toType, boolean overflow);

  SSAGetCaughtExceptionInstruction GetCaughtExceptionInstruction(int bbNumber, int exceptionValueNumber);

  SSAGetInstruction GetInstruction(int result, FieldReference field);

  SSAGetInstruction GetInstruction(int result, int ref, FieldReference field);

  SSAGotoInstruction GotoInstruction();

  SSAInstanceofInstruction InstanceofInstruction(int result, int ref, TypeReference checkedType);

  SSAInvokeInstruction InvokeInstruction(int result, int[] params, int exception, CallSiteReference site);

  SSAInvokeInstruction InvokeInstruction(int[] params, int exception, CallSiteReference site);

  SSALoadIndirectInstruction LoadIndirectInstruction(int lval, TypeReference t, int addressVal);
  
  SSALoadMetadataInstruction LoadMetadataInstruction(int lval, TypeReference entityType, Object token);

  SSAMonitorInstruction MonitorInstruction(int ref, boolean isEnter);

  SSANewInstruction NewInstruction(int result, NewSiteReference site);

  SSANewInstruction NewInstruction(int result, NewSiteReference site, int[] params);
  SSAPhiInstruction PhiInstruction(int result, int[] params);

  SSAPiInstruction PiInstruction(int result, int val, int piBlock, int successorBlock, SSAInstruction cause);

  SSAPutInstruction PutInstruction(int ref, int value, FieldReference field);

  SSAPutInstruction PutInstruction(int value, FieldReference field);

  SSAReturnInstruction ReturnInstruction();

  SSAReturnInstruction ReturnInstruction(int result, boolean isPrimitive);

  SSAStoreIndirectInstruction StoreIndirectInstruction(int addressVal, int rval, TypeReference pointeeType);
  
  SSASwitchInstruction SwitchInstruction(int val, int defaultLabel, int[] casesAndLabels);

  SSAThrowInstruction ThrowInstruction(int exception);

  SSAUnaryOpInstruction UnaryOpInstruction(IUnaryOpInstruction.IOperator operator, int result, int val);

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IComparisonInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction;
import com.ibm.wala.shrikeBT.IUnaryOpInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;

/**
 * An instruction factory for SSA. 
 */
public interface SSAInstructionFactory {

  SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, TypeReference pointeeType);

  SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, int indexVal, TypeReference pointeeType);

  SSAAddressOfInstruction AddressOfInstruction(int iindex, int lval, int local, FieldReference field, TypeReference pointeeType);
  
  SSAArrayLengthInstruction ArrayLengthInstruction(int iindex, int result, int arrayref);

  SSAArrayLoadInstruction ArrayLoadInstruction(int iindex, int result, int arrayref, int index, TypeReference declaredType);

  SSAArrayStoreInstruction ArrayStoreInstruction(int iindex, int arrayref, int index, int value, TypeReference declaredType);

  SSABinaryOpInstruction BinaryOpInstruction(int iindex, IBinaryOpInstruction.IOperator operator, boolean overflow, boolean unsigned,
      int result, int val1, int val2, boolean mayBeInteger);

  SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, int[] typeValues, boolean isPEI);

  SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, int typeValue, boolean isPEI);

  SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, TypeReference[] types, boolean isPEI);

  SSACheckCastInstruction CheckCastInstruction(int iindex, int result, int val, TypeReference type, boolean isPEI);

  SSAComparisonInstruction ComparisonInstruction(int iindex, IComparisonInstruction.Operator operator, int result, int val1, int val2);

  SSAConditionalBranchInstruction ConditionalBranchInstruction(int iindex, IConditionalBranchInstruction.IOperator operator,
      TypeReference type, int val1, int val2);

  SSAConversionInstruction ConversionInstruction(int iindex, int result, int val, TypeReference fromType, TypeReference toType, boolean overflow);

  SSAGetCaughtExceptionInstruction GetCaughtExceptionInstruction(int iindex, int bbNumber, int exceptionValueNumber);

  SSAGetInstruction GetInstruction(int iindex, int result, FieldReference field);

  SSAGetInstruction GetInstruction(int iindex, int result, int ref, FieldReference field);

  SSAGotoInstruction GotoInstruction(int iindex);

  SSAInstanceofInstruction InstanceofInstruction(int iindex, int result, int ref, TypeReference checkedType);

  SSAInvokeInstruction InvokeInstruction(int iindex, int result, int[] params, int exception, CallSiteReference site);

  SSAInvokeInstruction InvokeInstruction(int iindex, int[] params, int exception, CallSiteReference site);

  SSALoadIndirectInstruction LoadIndirectInstruction(int iindex, int lval, TypeReference t, int addressVal);
  
  SSALoadMetadataInstruction LoadMetadataInstruction(int iindex, int lval, TypeReference entityType, Object token);

  SSAMonitorInstruction MonitorInstruction(int iindex, int ref, boolean isEnter);

  SSANewInstruction NewInstruction(int iindex, int result, NewSiteReference site);

  SSANewInstruction NewInstruction(int iindex, int result, NewSiteReference site, int[] params);

  SSAPhiInstruction PhiInstruction(int iindex, int result, int[] params);

  SSAPiInstruction PiInstruction(int iindex, int result, int val, int piBlock, int successorBlock, SSAInstruction cause);

  SSAPutInstruction PutInstruction(int iindex, int ref, int value, FieldReference field);

  SSAPutInstruction PutInstruction(int iindex, int value, FieldReference field);

  SSAReturnInstruction ReturnInstruction(int iindex);

  SSAReturnInstruction ReturnInstruction(int iindex, int result, boolean isPrimitive);

  SSAStoreIndirectInstruction StoreIndirectInstruction(int iindex, int addressVal, int rval, TypeReference pointeeType);
  
  SSASwitchInstruction SwitchInstruction(int iindex, int val, int defaultLabel, int[] casesAndLabels);

  SSAThrowInstruction ThrowInstruction(int iindex, int exception);

  SSAUnaryOpInstruction UnaryOpInstruction(int iindex, IUnaryOpInstruction.IOperator operator, int result, int val);

}
File
SSAInstructionFactory.java
Developer's decision
Version 1
Kind of conflict
Comment
Import
Interface declaration
Package declaration
Chunk
Conflicting content
   */
    assertParamsKosher(result, params, site);
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.types.TypeReference;

/**
 */
public abstract class SSAInvokeInstruction extends SSAAbstractInvokeInstruction {

  private final int result;

  /**
   * The value numbers of the arguments passed to the call. For non-static methods, params[0] == this. If params == null, this
   * should be a static method with no parameters.
   */
  private final int[] params;

  protected SSAInvokeInstruction(int index, int result, int[] params, int exception, CallSiteReference site) {
    super(index, exception, site);
    this.result = result;
    this.params = params;
  }

  /**
   * Constructor InvokeInstruction. This case for void return values
   */
  protected SSAInvokeInstruction(int index, int[] params, int exception, CallSiteReference site) {
    this(index, -1, params, exception, site);
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    // result == -1 for void-returning methods, which are the only calls
    // that have a single value def.
    return insts.InvokeInstruction(iindex, defs == null || result == -1 ? result : defs[0], uses == null ? params : uses,
        defs == null ? exception : defs[result == -1 ? 0 : 1], site);
  }

  public static void assertParamsKosher(int result, int[] params, CallSiteReference site) throws IllegalArgumentException {
    if (site == null) {
      throw new IllegalArgumentException("site cannot be null");
    }
    if (site.getDeclaredTarget().getReturnType().equals(TypeReference.Void)) {
      if (result != -1) {
        assert result == -1 : "bogus call to " + site;
      }
    }

    int nExpected = 0;
    if (!site.isStatic()) {
      nExpected++;
    }

    nExpected += site.getDeclaredTarget().getNumberOfParameters();
    if (nExpected > 0) {
      if (params == null) {
        assert params != null : "null params for " + site;
      }
      if (params.length != nExpected) {
        assert params.length == nExpected : "wrong number of params for " + site + " Expected " + nExpected + " got "
            + params.length;
      }
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitInvoke(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    if (params == null) {
      assert site.getInvocationCode() == IInvokeInstruction.Dispatch.STATIC
          || site.getInvocationCode() == IInvokeInstruction.Dispatch.SPECIAL;
      assert site.getDeclaredTarget().getNumberOfParameters() == 0;
      return 0;
    } else {
      return params.length;
    }
  }

  @Override
  public int getNumberOfParameters() {
    return getNumberOfUses();
  }

  @Override
  public int getNumberOfReturnValues() {
    return (result == -1) ? 0 : 1;
  }

  @Override
  public int getReturnValue(int i) {
    assert i == 0 && result != -1;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (params == null) {
      assert false : "Invalid getUse: " + j + " , null params " + this;
    }
    if (params.length <= j) {
      assert params.length > j : "Invalid getUse: " + this + ", index " + j + ", params.length " + params.length;
    }
    return params[j];
  }

  @Override
  public int hashCode() {
    return (site.hashCode() * 7529) + (exception * 9823);
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.types.TypeReference;

/**
 */
public abstract class SSAInvokeInstruction extends SSAAbstractInvokeInstruction {

  private final int result;

  /**
   * The value numbers of the arguments passed to the call. For non-static methods, params[0] == this. If params == null, this
   * should be a static method with no parameters.
   */
  private final int[] params;

  protected SSAInvokeInstruction(int result, int[] params, int exception, CallSiteReference site) {
    super(exception, site);
    this.result = result;
    this.params = params;
    assertParamsKosher(result, params, site);
  }

  /**
   * Constructor InvokeInstruction. This case for void return values
  protected SSAInvokeInstruction(int[] params, int exception, CallSiteReference site) {
    this(-1, params, exception, site);
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    // result == -1 for void-returning methods, which are the only calls
    // that have a single value def.
    return insts.InvokeInstruction(defs == null || result == -1 ? result : defs[0], uses == null ? params : uses,
        defs == null ? exception : defs[result == -1 ? 0 : 1], site);
  }

  public static void assertParamsKosher(int result, int[] params, CallSiteReference site) throws IllegalArgumentException {
    if (site == null) {
      throw new IllegalArgumentException("site cannot be null");
    }
    if (site.getDeclaredTarget().getReturnType().equals(TypeReference.Void)) {
      if (result != -1) {
        assert result == -1 : "bogus call to " + site;
      }
    }

    int nExpected = 0;
    if (!site.isStatic()) {
      nExpected++;
    }

    nExpected += site.getDeclaredTarget().getNumberOfParameters();
    if (nExpected > 0) {
      if (params == null) {
        assert params != null : "null params for " + site;
      }
      if (params.length != nExpected) {
        assert params.length == nExpected : "wrong number of params for " + site + " Expected " + nExpected + " got "
            + params.length;
      }
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitInvoke(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    if (params == null) {
      assert site.getInvocationCode() == IInvokeInstruction.Dispatch.STATIC
          || site.getInvocationCode() == IInvokeInstruction.Dispatch.SPECIAL;
      assert site.getDeclaredTarget().getNumberOfParameters() == 0;
      return 0;
    } else {
      return params.length;
    }
  }

  @Override
  public int getNumberOfParameters() {
    return getNumberOfUses();
  }

  @Override
  public int getNumberOfReturnValues() {
    return (result == -1) ? 0 : 1;
  }

  @Override
  public int getReturnValue(int i) {
    assert i == 0 && result != -1;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (params == null) {
      assert false : "Invalid getUse: " + j + " , null params " + this;
    }
    if (params.length <= j) {
      assert params.length > j : "Invalid getUse: " + this + ", index " + j + ", params.length " + params.length;
    }
    return params[j];
  }

  @Override
  public int hashCode() {
    return (site.hashCode() * 7529) + (exception * 9823);
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.shrikeBT.IInvokeInstruction;
import com.ibm.wala.types.TypeReference;

/**
 */
public abstract class SSAInvokeInstruction extends SSAAbstractInvokeInstruction {

  private final int result;

  /**
   * The value numbers of the arguments passed to the call. For non-static methods, params[0] == this. If params == null, this
   * should be a static method with no parameters.
   */
  private final int[] params;

  protected SSAInvokeInstruction(int index, int result, int[] params, int exception, CallSiteReference site) {
    super(index, exception, site);
    this.result = result;
    this.params = params;
    assertParamsKosher(result, params, site);
  }

  /**
   * Constructor InvokeInstruction. This case for void return values
   */
  protected SSAInvokeInstruction(int index, int[] params, int exception, CallSiteReference site) {
    this(index, -1, params, exception, site);
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    // result == -1 for void-returning methods, which are the only calls
    // that have a single value def.
    return insts.InvokeInstruction(iindex, defs == null || result == -1 ? result : defs[0], uses == null ? params : uses,
        defs == null ? exception : defs[result == -1 ? 0 : 1], site);
  }

  public static void assertParamsKosher(int result, int[] params, CallSiteReference site) throws IllegalArgumentException {
    if (site == null) {
      throw new IllegalArgumentException("site cannot be null");
    }
    if (site.getDeclaredTarget().getReturnType().equals(TypeReference.Void)) {
      if (result != -1) {
        assert result == -1 : "bogus call to " + site;
      }
    }

    int nExpected = 0;
    if (!site.isStatic()) {
      nExpected++;
    }

    nExpected += site.getDeclaredTarget().getNumberOfParameters();
    if (nExpected > 0) {
      if (params == null) {
        assert params != null : "null params for " + site;
      }
      if (params.length != nExpected) {
        assert params.length == nExpected : "wrong number of params for " + site + " Expected " + nExpected + " got "
            + params.length;
      }
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitInvoke(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    if (params == null) {
      assert site.getInvocationCode() == IInvokeInstruction.Dispatch.STATIC
          || site.getInvocationCode() == IInvokeInstruction.Dispatch.SPECIAL;
      assert site.getDeclaredTarget().getNumberOfParameters() == 0;
      return 0;
    } else {
      return params.length;
    }
  }

  @Override
  public int getNumberOfParameters() {
    return getNumberOfUses();
  }

  @Override
  public int getNumberOfReturnValues() {
    return (result == -1) ? 0 : 1;
  }

  @Override
  public int getReturnValue(int i) {
    assert i == 0 && result != -1;
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (params == null) {
      assert false : "Invalid getUse: " + j + " , null params " + this;
    }
    if (params.length <= j) {
      assert params.length > j : "Invalid getUse: " + this + ", index " + j + ", params.length " + params.length;
    }
    return params[j];
  }

  @Override
  public int hashCode() {
    return (site.hashCode() * 7529) + (exception * 9823);
  }

}
File
SSAInvokeInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * An instruction that represents a reflective or meta-programming operation, like loadClass in Java
 */
public abstract class SSALoadMetadataInstruction extends SSAInstruction {

  private final int lval;

  /**
   * A representation of the meta-data itself. For a loadClass operation, this would be a {@link TypeReference} representing the
   * class object being manipulated
   */
  private final Object token;

  /**
   * The type of the thing that this meta-data represents. For a loadClass instruction, entityType is java.lang.Class
   */
  private final TypeReference entityType;

  protected SSALoadMetadataInstruction(int index, int lval, TypeReference entityType, Object token) {
    super(index);
    this.lval = lval;
    this.token = token;
    this.entityType = entityType;
    if (token == null) {
      throw new IllegalArgumentException("null typeRef");
    }
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("(defs != null) and (defs.length == 0)");
    }
    return insts.LoadMetadataInstruction(iindex, defs == null ? lval : defs[0], entityType, token);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, lval) + " = load_metadata: " + token + ", " + entityType;
  }

  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitLoadMetadata(this);
  }

  @Override
  public int hashCode() {
    return token.hashCode() * lval;
  }

  @Override
  public boolean isPEI() {
    return true;
  }

  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return lval;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return lval;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public boolean isFallThrough() {
    return true;
  }

  public Object getToken() {
    return token;
  }

  public TypeReference getType() {
    return entityType;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * An instruction that represents a reflective or meta-programming operation, like loadClass in Java
 */
public abstract class SSALoadMetadataInstruction extends SSAInstruction {

  private final int lval;

  /**
   * A representation of the meta-data itself. For a loadClass operation, this would be a {@link TypeReference} representing the
   * class object being manipulated
   */
  private final Object token;

  /**
   * The type of the thing that this meta-data represents. For a loadClass instruction, entityType is java.lang.Class
   */
  private final TypeReference entityType;

  protected SSALoadMetadataInstruction(int lval, TypeReference entityType, Object token) {
    this.lval = lval;
    this.token = token;
    this.entityType = entityType;
    if (token == null) {
      throw new IllegalArgumentException("null typeRef");
    }
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("(defs != null) and (defs.length == 0)");
    }
    return insts.LoadMetadataInstruction(defs == null ? lval : defs[0], entityType, token);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, lval) + " = load_metadata: " + token + ", " + entityType;
  }

  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitLoadMetadata(this);
  }

  @Override
  public int hashCode() {
    return token.hashCode() * lval;
  }

  @Override
  public boolean isPEI() {
    return true;
  }

  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return lval;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return lval;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public boolean isFallThrough() {
    return true;
  }

  public Object getToken() {
    return token;
  }

  public TypeReference getType() {
    return entityType;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.TypeReference;

/**
 * An instruction that represents a reflective or meta-programming operation, like loadClass in Java
 */
public abstract class SSALoadMetadataInstruction extends SSAInstruction {

  private final int lval;

  /**
   * A representation of the meta-data itself. For a loadClass operation, this would be a {@link TypeReference} representing the
   * class object being manipulated
   */
  private final Object token;

  /**
   * The type of the thing that this meta-data represents. For a loadClass instruction, entityType is java.lang.Class
   */
  private final TypeReference entityType;

  protected SSALoadMetadataInstruction(int index, int lval, TypeReference entityType, Object token) {
    super(index);
    this.lval = lval;
    this.token = token;
    this.entityType = entityType;
    if (token == null) {
      throw new IllegalArgumentException("null typeRef");
    }
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException("(defs != null) and (defs.length == 0)");
    }
    return insts.LoadMetadataInstruction(iindex, defs == null ? lval : defs[0], entityType, token);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, lval) + " = load_metadata: " + token + ", " + entityType;
  }

  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitLoadMetadata(this);
  }

  @Override
  public int hashCode() {
    return token.hashCode() * lval;
  }

  @Override
  public boolean isPEI() {
    return true;
  }

  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return lval;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return lval;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  @Override
  public boolean isFallThrough() {
    return true;
  }

  public Object getToken() {
    return token;
  }

  public TypeReference getType() {
    return entityType;
  }
}
File
SSALoadMetadataInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * An instruction representing a monitorenter or monitorexit operation.
 */
public abstract class SSAMonitorInstruction extends SSAInstruction {
  /**
   * The value number of the object being locked or unlocked
   */
  private final int ref;

  /**
   * Does this instruction represent a monitorenter?
   */
  private final boolean isEnter;

  /**
   * @param ref The value number of the object being locked or unlocked
   * @param isEnter Does this instruction represent a monitorenter?
   */
  protected SSAMonitorInstruction(int index, int ref, boolean isEnter) {
    super(index);
    this.ref = ref;
    this.isEnter = isEnter;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    assert uses == null || uses.length == 1;
    return insts.MonitorInstruction(iindex, uses == null ? ref : uses[0], isEnter);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "monitor" + (isEnter ? "enter " : "exit ") + getValueString(symbolTable, ref);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitMonitor(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return ref;
  }

  @Override
  public int hashCode() {
    return ref * 6173 + 4423;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  /**
   * @return The value number of the object being locked or unlocked
   */
  public int getRef() {
    return ref;
  }

  /**
   * Does this instruction represent a monitorenter?
   */
  public boolean isMonitorEnter() {
    return isEnter;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * An instruction representing a monitorenter or monitorexit operation.
 */
public abstract class SSAMonitorInstruction extends SSAInstruction {
  /**
   * The value number of the object being locked or unlocked
   */
  private final int ref;

  /**
   * Does this instruction represent a monitorenter?
   */
  private final boolean isEnter;

  /**
   * @param ref The value number of the object being locked or unlocked
   * @param isEnter Does this instruction represent a monitorenter?
   */
  protected SSAMonitorInstruction(int ref, boolean isEnter) {
    super();
    this.ref = ref;
    this.isEnter = isEnter;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    assert uses == null || uses.length == 1;
    return insts.MonitorInstruction(uses == null ? ref : uses[0], isEnter);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "monitor" + (isEnter ? "enter " : "exit ") + getValueString(symbolTable, ref);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitMonitor(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return ref;
  }

  @Override
  public int hashCode() {
    return ref * 6173 + 4423;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  /**
   * @return The value number of the object being locked or unlocked
   */
  public int getRef() {
    return ref;
  }

  /**
   * Does this instruction represent a monitorenter?
   */
  public boolean isMonitorEnter() {
    return isEnter;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;


/**
 * An instruction representing a monitorenter or monitorexit operation.
 */
public abstract class SSAMonitorInstruction extends SSAInstruction {
  /**
   * The value number of the object being locked or unlocked
   */
  private final int ref;

  /**
   * Does this instruction represent a monitorenter?
   */
  private final boolean isEnter;

  /**
   * @param ref The value number of the object being locked or unlocked
   * @param isEnter Does this instruction represent a monitorenter?
   */
  protected SSAMonitorInstruction(int index, int ref, boolean isEnter) {
    super(index);
    this.ref = ref;
    this.isEnter = isEnter;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    assert uses == null || uses.length == 1;
    return insts.MonitorInstruction(iindex, uses == null ? ref : uses[0], isEnter);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return "monitor" + (isEnter ? "enter " : "exit ") + getValueString(symbolTable, ref);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitMonitor(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0;
    return ref;
  }

  @Override
  public int hashCode() {
    return ref * 6173 + 4423;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

  /**
   * @return The value number of the object being locked or unlocked
   */
  public int getRef() {
    return ref;
  }

  /**
   * Does this instruction represent a monitorenter?
   */
  public boolean isMonitorEnter() {
    return isEnter;
  }
}
File
SSAMonitorInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.TypeReference;

/**
 * An allocation instruction ("new") for SSA form.
 * 
 * This includes allocations of both scalars and arrays.
 */
public abstract class SSANewInstruction extends SSAInstruction {
  private final int result;

  private final NewSiteReference site;

  /**
   * The value numbers of the arguments passed to the call. If params == null, this should be a static this statement allocates a
   * scalar. if params != null, then params[i] is the size of the ith dimension of the array.
   */
  private final int[] params;

  /**
   * Create a new instruction to allocate a scalar.
   */
  protected SSANewInstruction(int index, int result, NewSiteReference site) throws IllegalArgumentException {
    super(index);
    if (site == null) {
      throw new IllegalArgumentException("site cannot be null");
    }
    assert !site.getDeclaredType().isArrayType()
        || site.getDeclaredType().getClassLoader().getLanguage() != ClassLoaderReference.Java;
    this.result = result;
    this.site = site;
    this.params = null;
  }

  /**
   * Create a new instruction to allocate an array.
   * 
   * @throws IllegalArgumentException if site is null
   * @throws IllegalArgumentException if params is null
   */
  protected SSANewInstruction(int index, int result, NewSiteReference site, int[] params) {
    super(index);
    if (params == null) {
      throw new IllegalArgumentException("params is null");
    }
    if (site == null) {
      throw new IllegalArgumentException("site is null");
    }
    assert site.getDeclaredType().isArrayType()
        || site.getDeclaredType().getClassLoader().getLanguage() != ClassLoaderReference.Java;
    this.result = result;
    this.site = site;
    this.params = new int[params.length];
    System.arraycopy(params, 0, this.params, 0, params.length);
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (params == null) {
      return insts.NewInstruction(iindex, defs == null ? result : defs[0], site);
    } else {
      return insts.NewInstruction(iindex, defs == null ? result : defs[0], site, uses == null ? params : uses);
    }
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = new " + site.getDeclaredType() + "@" + site.getProgramCounter()
        + (params == null ? "" : array2String(params, symbolTable));
  }

  private String array2String(int[] params, SymbolTable symbolTable) {
    StringBuffer result = new StringBuffer();
    for (int i = 0; i < params.length; i++) {
      result.append(getValueString(symbolTable, params[i]));
      result.append(" ");
    }
    return result.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitNew(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  /**
   * @return TypeReference
   */
  public TypeReference getConcreteType() {
    return site.getDeclaredType();
  }

  public NewSiteReference getNewSite() {
    return site;
  }

  @Override
  public int hashCode() {
    return result * 7529 + site.getDeclaredType().hashCode();
  }

  @Override
  public int getNumberOfUses() {
    return params == null ? 0 : params.length;
  }

  @Override
  public int getUse(int j) {
    assert params != null : "expected params but got null";
    assert params.length > j : "found too few parameters";
    return params[j];
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.TypeReference;

/**
 * An allocation instruction ("new") for SSA form.
 * 
 * This includes allocations of both scalars and arrays.
 */
public abstract class SSANewInstruction extends SSAInstruction {
  private final int result;

  private final NewSiteReference site;

  /**
   * The value numbers of the arguments passed to the call. If params == null, this should be a static this statement allocates a
   * scalar. if params != null, then params[i] is the size of the ith dimension of the array.
   */
  private final int[] params;

  /**
   * Create a new instruction to allocate a scalar.
   */
  protected SSANewInstruction(int result, NewSiteReference site) throws IllegalArgumentException {
    super();
    if (site == null) {
      throw new IllegalArgumentException("site cannot be null");
    }
    this.result = result;
    this.site = site;
    this.params = null;
  }

  /**
   * Create a new instruction to allocate an array.
   * 
   * @throws IllegalArgumentException if site is null
   * @throws IllegalArgumentException if params is null
   */
  protected SSANewInstruction(int result, NewSiteReference site, int[] params) {
    super();
    if (params == null) {
      throw new IllegalArgumentException("params is null");
    }
    if (site == null) {
      throw new IllegalArgumentException("site is null");
    }
    assert site.getDeclaredType().isArrayType()
        || site.getDeclaredType().getClassLoader().getLanguage() != ClassLoaderReference.Java;
    this.result = result;
    this.site = site;
    this.params = new int[params.length];
    System.arraycopy(params, 0, this.params, 0, params.length);
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (params == null) {
      return insts.NewInstruction(defs == null ? result : defs[0], site);
    } else {
      return insts.NewInstruction(defs == null ? result : defs[0], site, uses == null ? params : uses);
    }
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = new " + site.getDeclaredType() + "@" + site.getProgramCounter()
        + (params == null ? "" : array2String(params, symbolTable));
  }

  private String array2String(int[] params, SymbolTable symbolTable) {
    StringBuffer result = new StringBuffer();
    for (int i = 0; i < params.length; i++) {
      result.append(getValueString(symbolTable, params[i]));
      result.append(" ");
    }
    return result.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitNew(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  /**
   * @return TypeReference
   */
  public TypeReference getConcreteType() {
    return site.getDeclaredType();
  }

  public NewSiteReference getNewSite() {
    return site;
  }

  @Override
  public int hashCode() {
    return result * 7529 + site.getDeclaredType().hashCode();
  }

  @Override
  public int getNumberOfUses() {
    return params == null ? 0 : params.length;
  }

  @Override
  public int getUse(int j) {
    assert params != null : "expected params but got null";
    assert params.length > j : "found too few parameters";
    return params[j];
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.TypeReference;
/**
 * An allocation instruction ("new") for SSA form.
 * 
 * This includes allocations of both scalars and arrays.
 */
public abstract class SSANewInstruction extends SSAInstruction {
  private final int result;

  private final NewSiteReference site;

  /**
   * The value numbers of the arguments passed to the call. If params == null, this should be a static this statement allocates a
   * scalar. if params != null, then params[i] is the size of the ith dimension of the array.
   */
  private final int[] params;

  /**
   * Create a new instruction to allocate a scalar.
   */
  protected SSANewInstruction(int index, int result, NewSiteReference site) throws IllegalArgumentException {
    super(index);
    if (site == null) {
      throw new IllegalArgumentException("site cannot be null");
    }
    this.result = result;
    this.site = site;
    this.params = null;
  }

  /**
   * Create a new instruction to allocate an array.
   * 
   * @throws IllegalArgumentException if site is null
   * @throws IllegalArgumentException if params is null
   */
  protected SSANewInstruction(int index, int result, NewSiteReference site, int[] params) {
    super(index);
    if (params == null) {
      throw new IllegalArgumentException("params is null");
    }
    if (site == null) {
      throw new IllegalArgumentException("site is null");
    }
    assert site.getDeclaredType().isArrayType()
        || site.getDeclaredType().getClassLoader().getLanguage() != ClassLoaderReference.Java;
    this.result = result;
    this.site = site;
    this.params = new int[params.length];
    System.arraycopy(params, 0, this.params, 0, params.length);
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (params == null) {
      return insts.NewInstruction(iindex, defs == null ? result : defs[0], site);
    } else {
      return insts.NewInstruction(iindex, defs == null ? result : defs[0], site, uses == null ? params : uses);
    }
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = new " + site.getDeclaredType() + "@" + site.getProgramCounter()
        + (params == null ? "" : array2String(params, symbolTable));
  }

  private String array2String(int[] params, SymbolTable symbolTable) {
    StringBuffer result = new StringBuffer();
    for (int i = 0; i < params.length; i++) {
      result.append(getValueString(symbolTable, params[i]));
      result.append(" ");
    }
    return result.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitNew(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  /**
   * @return TypeReference
   */
  public TypeReference getConcreteType() {
    return site.getDeclaredType();
  }

  public NewSiteReference getNewSite() {
    return site;
  }

  @Override
  public int hashCode() {
    return result * 7529 + site.getDeclaredType().hashCode();
  }

  @Override
  public int getNumberOfUses() {
    return params == null ? 0 : params.length;
  }

  @Override
  public int getUse(int j) {
    assert params != null : "expected params but got null";
    assert params.length > j : "found too few parameters";
    return params[j];
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isPEI()
   */
  @Override
  public boolean isPEI() {
    return true;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

}
File
SSANewInstruction.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
  @Override
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine;

/**
 * A phi instruction in SSA form.
 * 
 * See any modern compiler textbook for the definition of phi and the nature of SSA.
 * 
 * Note: In SSA {@link IR}s, these instructions do not appear in the normal instruction array returned by IR.getInstructions();
 * instead these instructions live in {@link ISSABasicBlock}.
 * 
 * Note: if getUse(i) returns {@link AbstractIntStackMachine}.TOP (that is, -1), then that use represents an edge in the CFG which
 * is infeasible in verifiable bytecode.
 */
public class SSAPhiInstruction extends SSAInstruction {
  private final int result;

  private int[] params;

  public SSAPhiInstruction(int index, int result, int[] params) throws IllegalArgumentException {
    super(index);
    if (params == null) {
      throw new IllegalArgumentException("params is null");
    }
    this.result = result;
    this.params = params;
    if (params.length == 0) {
      throw new IllegalArgumentException("can't have phi with no params");
    }
    for (int p : params) {
      if (p == 0) {
        throw new IllegalArgumentException("zero is an invalid value number for a parameter to phi");
      }
    }
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException();
    }
    return insts.PhiInstruction(iindex, defs == null ? result : defs[0], uses == null ? params : uses);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    StringBuffer s = new StringBuffer();

    s.append(getValueString(symbolTable, result)).append(" = phi ");
    s.append(" ").append(getValueString(symbolTable, params[0]));
    for (int i = 1; i < params.length; i++) {
      s.append(",").append(getValueString(symbolTable, params[i]));
    }
    return s.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitPhi(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    if (i != 0) {
      throw new IllegalArgumentException("invalid i: " + i);
    }
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return params.length;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) throws IllegalArgumentException {
    if (j >= params.length || j < 0) {
      throw new IllegalArgumentException("Bad use " + j);
    }
    return params[j];
  }

  /**
   * Clients should not call this.  only for SSA builders.
   * I hate this. Nuke it someday.
   */
  public void setValues(int[] i) {
    if (i == null || i.length < 1) {
      throw new IllegalArgumentException("illegal i: " + i);
    }
    this.params = i;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getValueString(SymbolTable, ValueDecorator, int)
   */
  @Override
  protected String getValueString(SymbolTable symbolTable, int valueNumber) {
    if (valueNumber == AbstractIntStackMachine.TOP) {
      return "TOP";
    } else {
      return super.getValueString(symbolTable, valueNumber);
    }
  }

  public int hashCode() {
    return 7823 * result;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

 }
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine;

/**
 * A phi instruction in SSA form.
 * 
 * See any modern compiler textbook for the definition of phi and the nature of SSA.
 * 
 * Note: In SSA {@link IR}s, these instructions do not appear in the normal instruction array returned by IR.getInstructions();
 * instead these instructions live in {@link ISSABasicBlock}.
 * 
 * Note: if getUse(i) returns {@link AbstractIntStackMachine}.TOP (that is, -1), then that use represents an edge in the CFG which
 * is infeasible in verifiable bytecode.
 */
public class SSAPhiInstruction extends SSAInstruction {
  private final int result;

  private int[] params;

  public SSAPhiInstruction(int result, int[] params) throws IllegalArgumentException {
    super();
    if (params == null) {
      throw new IllegalArgumentException("params is null");
    }
    this.result = result;
    this.params = params;
    if (params.length == 0) {
      throw new IllegalArgumentException("can't have phi with no params");
    }
    for (int p : params) {
      if (p == 0) {
        throw new IllegalArgumentException("zero is an invalid value number for a parameter to phi");
      }
    }
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException();
    }
    return insts.PhiInstruction(defs == null ? result : defs[0], uses == null ? params : uses);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    StringBuffer s = new StringBuffer();

    s.append(getValueString(symbolTable, result)).append(" = phi ");
    s.append(" ").append(getValueString(symbolTable, params[0]));
    for (int i = 1; i < params.length; i++) {
      s.append(",").append(getValueString(symbolTable, params[i]));
    }
    return s.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitPhi(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    if (i != 0) {
      throw new IllegalArgumentException("invalid i: " + i);
    }
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return params.length;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) throws IllegalArgumentException {
    if (j >= params.length || j < 0) {
      throw new IllegalArgumentException("Bad use " + j);
    }
    return params[j];
  }

  /**
   * Clients should not call this.  only for SSA builders.
   * I hate this. Nuke it someday.
   */
  public void setValues(int[] i) {
    if (i == null || i.length < 1) {
      throw new IllegalArgumentException("illegal i: " + i);
    }
    this.params = i;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getValueString(SymbolTable, ValueDecorator, int)
   */
  @Override
  protected String getValueString(SymbolTable symbolTable, int valueNumber) {
    if (valueNumber == AbstractIntStackMachine.TOP) {
      return "TOP";
    } else {
      return super.getValueString(symbolTable, valueNumber);
    }
  }

  @Override
  public int hashCode() {
    return 7823 * result;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

 }
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine;

/**
 * A phi instruction in SSA form.
 * 
 * See any modern compiler textbook for the definition of phi and the nature of SSA.
 * 
 * Note: In SSA {@link IR}s, these instructions do not appear in the normal instruction array returned by IR.getInstructions();
 * instead these instructions live in {@link ISSABasicBlock}.
 * 
 * Note: if getUse(i) returns {@link AbstractIntStackMachine}.TOP (that is, -1), then that use represents an edge in the CFG which
 * is infeasible in verifiable bytecode.
 */
public class SSAPhiInstruction extends SSAInstruction {
  private final int result;

  private int[] params;

  public SSAPhiInstruction(int index, int result, int[] params) throws IllegalArgumentException {
    super(index);
    if (params == null) {
      throw new IllegalArgumentException("params is null");
    }
    this.result = result;
    this.params = params;
    if (params.length == 0) {
      throw new IllegalArgumentException("can't have phi with no params");
    }
    for (int p : params) {
      if (p == 0) {
        throw new IllegalArgumentException("zero is an invalid value number for a parameter to phi");
      }
    }
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (defs != null && defs.length == 0) {
      throw new IllegalArgumentException();
    }
    return insts.PhiInstruction(iindex, defs == null ? result : defs[0], uses == null ? params : uses);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    StringBuffer s = new StringBuffer();

    s.append(getValueString(symbolTable, result)).append(" = phi ");
    s.append(" ").append(getValueString(symbolTable, params[0]));
    for (int i = 1; i < params.length; i++) {
      s.append(",").append(getValueString(symbolTable, params[i]));
    }
    return s.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitPhi(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getDef()
   */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    if (i != 0) {
      throw new IllegalArgumentException("invalid i: " + i);
    }
    return result;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return params.length;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) throws IllegalArgumentException {
    if (j >= params.length || j < 0) {
      throw new IllegalArgumentException("Bad use " + j);
    }
    return params[j];
  }

  /**
   * Clients should not call this.  only for SSA builders.
   * I hate this. Nuke it someday.
   */
  public void setValues(int[] i) {
    if (i == null || i.length < 1) {
      throw new IllegalArgumentException("illegal i: " + i);
    }
    this.params = i;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getValueString(SymbolTable, ValueDecorator, int)
   */
  @Override
  protected String getValueString(SymbolTable symbolTable, int valueNumber) {
    if (valueNumber == AbstractIntStackMachine.TOP) {
      return "TOP";
    } else {
      return super.getValueString(symbolTable, valueNumber);
    }
  }

  @Override
  public int hashCode() {
    return 7823 * result;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }

 }
File
SSAPhiInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
  public void visit(IVisitor v) {
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * A Pi instruction is a dummy assignment inserted at the tail of a basic block, in order
 * to get a new variable name to associate with some flow-insensitive dataflow fact.
 * You can build an {@link IR} with or without Pi instructions, depending on {@link SSAOptions} selected.
 * 
 * A Pi instruction is linked to its "cause" instruction, which is usually a conditional 
 * branch.
 * 
 * for example, the following pseudo-code
 * 

* * boolean condition = (x instanceof Integer); * if (condition) { * S1; * else { * S2; * } * * * could be translated roughly as follows: * * * boolean condition = (x instanceof Integer); * LABEL1: if (condition) { * x_1 = pi(x, LABEL1); * S1; * else { * x_2 = pi(x, LABEL2); * S2; * } * */ public class SSAPiInstruction extends SSAUnaryOpInstruction { private final SSAInstruction cause; private final int successorBlock; private final int piBlock; /** * @param successorBlock the successor block; this PI assignment happens on the transition between this basic block and * the successor block. */ public SSAPiInstruction(int index, int result, int val, int piBlock, int successorBlock, SSAInstruction cause) { super(index, null, result, val); this.cause = cause; this.successorBlock = successorBlock; this.piBlock = piBlock; } @Override public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) { assert defs == null || defs.length == 1; assert uses == null || uses.length == 1; return insts.PiInstruction(iindex, defs == null ? result : defs[0], uses == null ? val : uses[0], piBlock, successorBlock, cause); } @Override public String toString(SymbolTable symbolTable) { return getValueString(symbolTable, result) + " = pi " + getValueString(symbolTable, val) + " for BB" + successorBlock + ", cause " + cause; } @Override if (v == null) { throw new IllegalArgumentException("v is null"); } v.visitPi(this); } public int getSuccessor() { return successorBlock; } public int getPiBlock() { return piBlock; } public SSAInstruction getCause() { return cause; } public int getVal() { return getUse(0); } } ======= /******************************************************************************* * Copyright (c) 2002 - 2006 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.ssa; /** * A Pi instruction is a dummy assignment inserted at the tail of a basic block, in order * to get a new variable name to associate with some flow-insensitive dataflow fact. * You can build an {@link IR} with or without Pi instructions, depending on {@link SSAOptions} selected. * * A Pi instruction is linked to its "cause" instruction, which is usually a conditional * branch. * * for example, the following pseudo-code *

* * boolean condition = (x instanceof Integer); * if (condition) { * S1; * else { * S2; * } * * * could be translated roughly as follows: * * * boolean condition = (x instanceof Integer); * LABEL1: if (condition) { * x_1 = pi(x, LABEL1); * S1; * else { * x_2 = pi(x, LABEL2); * S2; * } * */ public class SSAPiInstruction extends SSAUnaryOpInstruction { private final SSAInstruction cause; private final int successorBlock; private final int piBlock; /** * @param successorBlock the successor block; this PI assignment happens on the transition between this basic block and * the successor block. */ public SSAPiInstruction(int result, int val, int piBlock, int successorBlock, SSAInstruction cause) { super(null, result, val); this.cause = cause; this.successorBlock = successorBlock; this.piBlock = piBlock; } @Override public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) { assert defs == null || defs.length == 1; assert uses == null || uses.length == 1; return insts.PiInstruction(defs == null ? result : defs[0], uses == null ? val : uses[0], piBlock, successorBlock, cause); } @Override public String toString(SymbolTable symbolTable) { return getValueString(symbolTable, result) + " = pi " + getValueString(symbolTable, val) + " for BB" + successorBlock + ", cause " + cause; } @Override public void visit(IVisitor v) { if (v == null) { throw new IllegalArgumentException("v is null"); } v.visitPi(this); } public int getSuccessor() { return successorBlock; } public int getPiBlock() { return piBlock; } public SSAInstruction getCause() { return cause; } public int getVal() { return getUse(0); } } >>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6

Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * A Pi instruction is a dummy assignment inserted at the tail of a basic block, in order
 * to get a new variable name to associate with some flow-insensitive dataflow fact.
 * You can build an {@link IR} with or without Pi instructions, depending on {@link SSAOptions} selected.
 * 
 * A Pi instruction is linked to its "cause" instruction, which is usually a conditional 
 * branch.
 * 
 * for example, the following pseudo-code
 * 

* * boolean condition = (x instanceof Integer); * if (condition) { * S1; * else { * S2; * } * * * could be translated roughly as follows: * * * boolean condition = (x instanceof Integer); * LABEL1: if (condition) { * x_1 = pi(x, LABEL1); * S1; * else { * x_2 = pi(x, LABEL2); * S2; * } * */ public class SSAPiInstruction extends SSAUnaryOpInstruction { private final SSAInstruction cause; private final int successorBlock; private final int piBlock; /** * @param successorBlock the successor block; this PI assignment happens on the transition between this basic block and * the successor block. */ public SSAPiInstruction(int index, int result, int val, int piBlock, int successorBlock, SSAInstruction cause) { super(index, null, result, val); this.cause = cause; this.successorBlock = successorBlock; this.piBlock = piBlock; } @Override public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) { assert defs == null || defs.length == 1; assert uses == null || uses.length == 1; return insts.PiInstruction(iindex, defs == null ? result : defs[0], uses == null ? val : uses[0], piBlock, successorBlock, cause); } @Override public String toString(SymbolTable symbolTable) { return getValueString(symbolTable, result) + " = pi " + getValueString(symbolTable, val) + " for BB" + successorBlock + ", cause " + cause; } @Override public void visit(IVisitor v) { if (v == null) { throw new IllegalArgumentException("v is null"); } v.visitPi(this); } public int getSuccessor() { return successorBlock; } public int getPiBlock() { return piBlock; } public SSAInstruction getCause() { return cause; } public int getVal() { return getUse(0); } }

File
SSAPiInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.FieldReference;

/**
 * A putfield or putstatic instruction
 */
public abstract class SSAPutInstruction extends SSAFieldAccessInstruction {

  private final int val;

  protected SSAPutInstruction(int index, int ref, int val, FieldReference field) {
    super(index, field, ref);
    this.val = val;
  }

  protected SSAPutInstruction(int index, int val, FieldReference field) {
    super(index, field, -1);
    this.val = val;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (isStatic())
      return insts.PutInstruction(iindex, uses == null ? val : uses[0], getDeclaredField());
    else
      return insts.PutInstruction(iindex, uses == null ? getRef() : uses[0], uses == null ? val : uses[1], getDeclaredField());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    if (isStatic()) {
      return "putstatic " + getValueString(symbolTable, val) + " " + getDeclaredField();
    } else {
      throw new IllegalArgumentException("v is null");
      return "putfield " + getValueString(symbolTable, getRef()) + " = " + getValueString(symbolTable, val) + " "
          + getDeclaredField();
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitPut(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return isStatic() ? 1 : 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0 || (!isStatic() && j == 1);
    return (j == 0 && !isStatic()) ? getRef() : val;
  }

  public int getVal() {
    return val;
  }

  @Override
  public int hashCode() {
    return val * 9929 ^ 2063;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.FieldReference;

/**
 * A putfield or putstatic instruction
 */
public abstract class SSAPutInstruction extends SSAFieldAccessInstruction {

  private final int val;

  protected SSAPutInstruction(int ref, int val, FieldReference field) {
    super(field, ref);
    this.val = val;
  }

  protected SSAPutInstruction(int val, FieldReference field) {
    super(field, -1);
    this.val = val;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (isStatic())
      return insts.PutInstruction(uses == null ? val : uses[0], getDeclaredField());
    else
      return insts.PutInstruction(uses == null ? getRef() : uses[0], uses == null ? val : uses[1], getDeclaredField());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    if (isStatic()) {
      return "putstatic " + getValueString(symbolTable, val) + " " + getDeclaredField();
    } else {
      return "putfield " + getValueString(symbolTable, getRef()) + " = " + getValueString(symbolTable, val) + " "
          + getDeclaredField();
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
    }
    v.visitPut(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return isStatic() ? 1 : 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0 || (!isStatic() && j == 1);
    return (j == 0 && !isStatic()) ? getRef() : val;
  }

  public int getVal() {
    return val;
  }

  @Override
  public int hashCode() {
    return val * 9929 ^ 2063;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.types.FieldReference;

/**
 * A putfield or putstatic instruction
 */
public abstract class SSAPutInstruction extends SSAFieldAccessInstruction {

  private final int val;

  protected SSAPutInstruction(int index, int ref, int val, FieldReference field) {
    super(index, field, ref);
    this.val = val;
  }

  protected SSAPutInstruction(int index, int val, FieldReference field) {
    super(index, field, -1);
    this.val = val;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (isStatic())
      return insts.PutInstruction(iindex, uses == null ? val : uses[0], getDeclaredField());
    else
      return insts.PutInstruction(iindex, uses == null ? getRef() : uses[0], uses == null ? val : uses[1], getDeclaredField());
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    if (isStatic()) {
      return "putstatic " + getValueString(symbolTable, val) + " " + getDeclaredField();
    } else {
      return "putfield " + getValueString(symbolTable, getRef()) + " = " + getValueString(symbolTable, val) + " "
          + getDeclaredField();
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitPut(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return isStatic() ? 1 : 2;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j == 0 || (!isStatic() && j == 1);
    return (j == 0 && !isStatic()) ? getRef() : val;
  }

  public int getVal() {
    return val;
  }

  @Override
  public int hashCode() {
    return val * 9929 ^ 2063;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return true;
  }
}
File
SSAPutInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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
    return false;
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package com.ibm.wala.ssa;

/**
 * A return instruction.
 */
public class SSAReturnInstruction extends SSAInstruction {

  /**
   * value number of the result. By convention result == -1 means returns void.
   */
  private final int result;

  private final boolean isPrimitive;

  public SSAReturnInstruction(int index, int result, boolean isPrimitive) {
    super(index);
    this.result = result;
    this.isPrimitive = isPrimitive;
  }

  public SSAReturnInstruction(int index) {
    super(index);
    this.result = -1;
    this.isPrimitive = false;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (result == -1)
      return insts.ReturnInstruction(iindex);
    else {
      if (uses != null && uses.length != 1) {
        throw new IllegalArgumentException("invalid uses.  must have exactly one use.");
      }
      return insts.ReturnInstruction(iindex, uses == null ? result : uses[0], isPrimitive);
    }
  }

  @Override
  public String toString(SymbolTable table) {
    if (result == -1) {
      return "return";
    } else {
      return "return " + getValueString(table, result);
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitReturn(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return (result == -1) ? 0 : 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (j != 0) {
      throw new IllegalArgumentException("illegal j: " + j);
    }
    return result;
  }

  /**
   * @return true iff this return instruction returns a primitive value
   */
  public boolean returnsPrimitiveType() {
    return isPrimitive;
  }

  public int getResult() {
    return result;
  }

  public boolean returnsVoid() {
    return result == -1;
  }

  @Override
  public int hashCode() {
    return result * 8933;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return false;
  }

 }
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * A return instruction.
 */
public class SSAReturnInstruction extends SSAInstruction {

  /**
   * value number of the result. By convention result == -1 means returns void.
   */
  private final int result;

  private final boolean isPrimitive;

  public SSAReturnInstruction(int result, boolean isPrimitive) {
    super();
    this.result = result;
    this.isPrimitive = isPrimitive;
  }

  public SSAReturnInstruction() {
    super();
    this.result = -1;
    this.isPrimitive = false;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (result == -1)
      return insts.ReturnInstruction();
    else {
      if (uses != null && uses.length != 1) {
        throw new IllegalArgumentException("invalid uses.  must have exactly one use.");
      }
      return insts.ReturnInstruction(uses == null ? result : uses[0], isPrimitive);
    }
  }

  @Override
  public String toString(SymbolTable table) {
    if (result == -1) {
      return "return";
    } else {
      return "return " + getValueString(table, result);
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitReturn(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return (result == -1) ? 0 : 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (j != 0) {
      throw new IllegalArgumentException("illegal j: " + j);
    }
    return result;
  }

  /**
   * @return true iff this return instruction returns a primitive value
   */
  public boolean returnsPrimitiveType() {
    return isPrimitive;
  }

  public int getResult() {
    return result;
  }

  public boolean returnsVoid() {
    return result == -1;
  }

  @Override
  public int hashCode() {
    return result * 8933;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
  }

 }
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * A return instruction.
 */
public class SSAReturnInstruction extends SSAInstruction {

  /**
   * value number of the result. By convention result == -1 means returns void.
   */
  private final int result;

  private final boolean isPrimitive;

  public SSAReturnInstruction(int index, int result, boolean isPrimitive) {
    super(index);
    this.result = result;
    this.isPrimitive = isPrimitive;
  }

  public SSAReturnInstruction(int index) {
    super(index);
    this.result = -1;
    this.isPrimitive = false;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (result == -1)
      return insts.ReturnInstruction(iindex);
    else {
      if (uses != null && uses.length != 1) {
        throw new IllegalArgumentException("invalid uses.  must have exactly one use.");
      }
      return insts.ReturnInstruction(iindex, uses == null ? result : uses[0], isPrimitive);
    }
  }

  @Override
  public String toString(SymbolTable table) {
    if (result == -1) {
      return "return";
    } else {
      return "return " + getValueString(table, result);
    }
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitReturn(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return (result == -1) ? 0 : 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    if (j != 0) {
      throw new IllegalArgumentException("illegal j: " + j);
    }
    return result;
  }

  /**
   * @return true iff this return instruction returns a primitive value
   */
  public boolean returnsPrimitiveType() {
    return isPrimitive;
  }

  public int getResult() {
    return result;
  }

  public boolean returnsVoid() {
    return result == -1;
  }

  @Override
  public int hashCode() {
    return result * 8933;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return false;
  }

 }
File
SSAReturnInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.util.intset.IntIterator;

/**
 * SSA instruction representing a switch statement.
 */
public class SSASwitchInstruction extends SSAInstruction {
  private final int val;

  private final int defaultLabel;

  private final int[] casesAndLabels;

  /**
   * The labels in casesAndLabels represent instruction indices in the IR that each switch case branches to.
   */
  public SSASwitchInstruction(int index, int val, int defaultLabel, int[] casesAndLabels) {
    super(index);
    this.val = val;
    this.defaultLabel = defaultLabel;
    this.casesAndLabels = casesAndLabels;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    assert uses == null || uses.length == 1;
    return insts.SwitchInstruction(iindex, uses == null ? val : uses[0], defaultLabel, casesAndLabels);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    StringBuffer result = new StringBuffer("switch ");
    result.append(getValueString(symbolTable, val));
    result.append(" [");
    for (int i = 0; i < casesAndLabels.length - 1; i++) {
      result.append(casesAndLabels[i]);
      i++;
      result.append("->");
      result.append(casesAndLabels[i]);
      if (i < casesAndLabels.length - 2) {
        result.append(",");
      }
    }
    result.append("]");
    super();
    return result.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitSwitch(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return val;
  }

  // public int[] getTargets() {
  // // TODO Auto-generated method stub
  // Assertions.UNREACHABLE();
  // return null;
  // }

  public int getTarget(int caseValue) {
    for (int i = 0; i < casesAndLabels.length; i += 2)
      if (caseValue == casesAndLabels[i])
        return casesAndLabels[i + 1];

    return defaultLabel;
  }

  public int getDefault() {
    return defaultLabel;
  }

  public int[] getCasesAndLabels() {
    return casesAndLabels;
  }

  public IntIterator iterateLabels() {
    return new IntIterator() {
      private int i = 0;

      public boolean hasNext() {
        return i < casesAndLabels.length;
      }

      public int next() {
        int v = casesAndLabels[i];
        i += 2;
        return v;
      }
    };
  }

  @Override
  public int hashCode() {
    return val * 1663 + 3499;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return false;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.util.intset.IntIterator;

/**
 * SSA instruction representing a switch statement.
 */
public class SSASwitchInstruction extends SSAInstruction {
  private final int val;

  private final int defaultLabel;

  private final int[] casesAndLabels;

  /**
   * The labels in casesAndLabels represent instruction indices in the IR that each switch case branches to.
   */
  public SSASwitchInstruction(int val, int defaultLabel, int[] casesAndLabels) {
    this.val = val;
    this.defaultLabel = defaultLabel;
    this.casesAndLabels = casesAndLabels;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    assert uses == null || uses.length == 1;
    return insts.SwitchInstruction(uses == null ? val : uses[0], defaultLabel, casesAndLabels);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    StringBuffer result = new StringBuffer("switch ");
    result.append(getValueString(symbolTable, val));
    result.append(" [");
    for (int i = 0; i < casesAndLabels.length - 1; i++) {
      result.append(casesAndLabels[i]);
      i++;
      result.append("->");
      result.append(casesAndLabels[i]);
      if (i < casesAndLabels.length - 2) {
        result.append(",");
      }
    }
    result.append("]");
    return result.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitSwitch(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return val;
  }

  // public int[] getTargets() {
  // // TODO Auto-generated method stub
  // Assertions.UNREACHABLE();
  // return null;
  // }

  public int getTarget(int caseValue) {
    for (int i = 0; i < casesAndLabels.length; i += 2)
      if (caseValue == casesAndLabels[i])
        return casesAndLabels[i + 1];

    return defaultLabel;
  }

  public int getDefault() {
    return defaultLabel;
  }

  public int[] getCasesAndLabels() {
    return casesAndLabels;
  }

  public IntIterator iterateLabels() {
    return new IntIterator() {
      private int i = 0;

      public boolean hasNext() {
        return i < casesAndLabels.length;
      }

      public int next() {
        int v = casesAndLabels[i];
        i += 2;
        return v;
      }
    };
  }

  @Override
  public int hashCode() {
    return val * 1663 + 3499;
  }

  /*
   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return false;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.util.intset.IntIterator;

/**
 * SSA instruction representing a switch statement.
 */
  /*
public class SSASwitchInstruction extends SSAInstruction {
  private final int val;

  private final int defaultLabel;

  private final int[] casesAndLabels;

  /**
   * The labels in casesAndLabels represent instruction indices in the IR that each switch case branches to.
   */
  public SSASwitchInstruction(int index, int val, int defaultLabel, int[] casesAndLabels) {
    super(index);
    this.val = val;
    this.defaultLabel = defaultLabel;
    this.casesAndLabels = casesAndLabels;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    assert uses == null || uses.length == 1;
    return insts.SwitchInstruction(iindex, uses == null ? val : uses[0], defaultLabel, casesAndLabels);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    StringBuffer result = new StringBuffer("switch ");
    result.append(getValueString(symbolTable, val));
    result.append(" [");
    for (int i = 0; i < casesAndLabels.length - 1; i++) {
      result.append(casesAndLabels[i]);
      i++;
      result.append("->");
      result.append(casesAndLabels[i]);
      if (i < casesAndLabels.length - 2) {
        result.append(",");
      }
    }
    result.append("]");
    return result.toString();
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   * @throws IllegalArgumentException if v is null
   */
  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitSwitch(this);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getNumberOfUses()
   */
  @Override
  public int getNumberOfUses() {
    return 1;
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#getUse(int)
   */
  @Override
  public int getUse(int j) {
    assert j <= 1;
    return val;
  }

  // public int[] getTargets() {
  // // TODO Auto-generated method stub
  // Assertions.UNREACHABLE();
  // return null;
  // }

  public int getTarget(int caseValue) {
    for (int i = 0; i < casesAndLabels.length; i += 2)
      if (caseValue == casesAndLabels[i])
        return casesAndLabels[i + 1];

    return defaultLabel;
  }

  public int getDefault() {
    return defaultLabel;
  }

  public int[] getCasesAndLabels() {
    return casesAndLabels;
  }

  public IntIterator iterateLabels() {
    return new IntIterator() {
      private int i = 0;

      public boolean hasNext() {
        return i < casesAndLabels.length;
      }

      public int next() {
        int v = casesAndLabels[i];
        i += 2;
        return v;
      }
    };
  }

  @Override
  public int hashCode() {
    return val * 1663 + 3499;
  }

   * @see com.ibm.wala.ssa.Instruction#isFallThrough()
   */
  @Override
  public boolean isFallThrough() {
    return false;
  }

}
File
SSASwitchInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * An instruction which unconditionally throws an exception
 */
public abstract class SSAThrowInstruction extends SSAAbstractThrowInstruction {

  protected SSAThrowInstruction(int index, int exception) {
    super(index, exception);
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#copyForSSA(com.ibm.wala.ssa.SSAInstructionFactory, int[], int[])
   */
  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length != 1) {
      throw new IllegalArgumentException("if non-null, uses.length must be 1");
    }
    return insts.ThrowInstruction(iindex, uses == null ? getException() : uses[0]);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitThrow(this);
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * An instruction which unconditionally throws an exception
 */
public abstract class SSAThrowInstruction extends SSAAbstractThrowInstruction {

  protected SSAThrowInstruction(int exception) {
    super(exception);
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#copyForSSA(com.ibm.wala.ssa.SSAInstructionFactory, int[], int[])
   */
  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length != 1) {
      throw new IllegalArgumentException("if non-null, uses.length must be 1");
    }
    return insts.ThrowInstruction(uses == null ? getException() : uses[0]);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitThrow(this);
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

/**
 * An instruction which unconditionally throws an exception
 */
public abstract class SSAThrowInstruction extends SSAAbstractThrowInstruction {

  protected SSAThrowInstruction(int index, int exception) {
    super(index, exception);
  }

  /* 
   * @see com.ibm.wala.ssa.SSAInstruction#copyForSSA(com.ibm.wala.ssa.SSAInstructionFactory, int[], int[])
   */
  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length != 1) {
      throw new IllegalArgumentException("if non-null, uses.length must be 1");
    }
    return insts.ThrowInstruction(iindex, uses == null ? getException() : uses[0]);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitThrow(this);
  }
}
File
SSAThrowInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Package declaration
Chunk
Conflicting content
 * An SSA instruction for some unary operator.
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.IUnaryOpInstruction;

/**
 * 
 * @see IUnaryOpInstruction for a list of operators
 */
public class SSAUnaryOpInstruction extends SSAAbstractUnaryInstruction {

  private final IUnaryOpInstruction.IOperator operator;

  public SSAUnaryOpInstruction(int index, IUnaryOpInstruction.IOperator operator, int result, int val) {
    super(index, result, val);
    this.operator = operator;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length == 0) {
      throw new IllegalArgumentException("(uses != null) and (uses.length == 0)");
    }
    return insts.UnaryOpInstruction(iindex, operator, defs == null || defs.length == 0 ? result : defs[0], uses == null ? val : uses[0]);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = " + operator + " " + getValueString(symbolTable, val);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitUnaryOp(this);
  }

  public IUnaryOpInstruction.IOperator getOpcode() {
    return operator;
  }

 }
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.IUnaryOpInstruction;

/**
 * An SSA instruction for some unary operator.
 * 
 * @see IUnaryOpInstruction for a list of operators
 */
public class SSAUnaryOpInstruction extends SSAAbstractUnaryInstruction {

  private final IUnaryOpInstruction.IOperator operator;

  public SSAUnaryOpInstruction(IUnaryOpInstruction.IOperator operator, int result, int val) {
    super(result, val);
    this.operator = operator;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length == 0) {
      throw new IllegalArgumentException("(uses != null) and (uses.length == 0)");
    }
    return insts.UnaryOpInstruction(operator, defs == null || defs.length == 0 ? result : defs[0], uses == null ? val : uses[0]);
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = " + operator + " " + getValueString(symbolTable, val);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitUnaryOp(this);
  }

  public IUnaryOpInstruction.IOperator getOpcode() {
    return operator;
  }

 }
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
  @Override
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import com.ibm.wala.shrikeBT.IUnaryOpInstruction;

/**
 * An SSA instruction for some unary operator.
 * 
 * @see IUnaryOpInstruction for a list of operators
 */
public class SSAUnaryOpInstruction extends SSAAbstractUnaryInstruction {

  private final IUnaryOpInstruction.IOperator operator;

  public SSAUnaryOpInstruction(int index, IUnaryOpInstruction.IOperator operator, int result, int val) {
    super(index, result, val);
    this.operator = operator;
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) throws IllegalArgumentException {
    if (uses != null && uses.length == 0) {
      throw new IllegalArgumentException("(uses != null) and (uses.length == 0)");
    }
    return insts.UnaryOpInstruction(iindex, operator, defs == null || defs.length == 0 ? result : defs[0], uses == null ? val : uses[0]);
  }
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result) + " = " + operator + " " + getValueString(symbolTable, val);
  }

  /**
   * @see com.ibm.wala.ssa.SSAInstruction#visit(IVisitor)
   */
  @Override
  public void visit(IVisitor v) throws NullPointerException {
    v.visitUnaryOp(this);
  }

  public IUnaryOpInstruction.IOperator getOpcode() {
    return operator;
  }

 }
File
SSAUnaryOpInstruction.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
  public SymbolTable(int numberOfParameters) {
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import java.util.HashMap;

import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.debug.Assertions;

/**
 * A symbol table which associates information with each variable (value number) in an SSA IR.
 * 
 * By convention, symbol numbers start at 1 ... the "this" parameter will be symbol number 1 in a virtual method.
 * 
 * This class is used heavily during SSA construction by {@link SSABuilder}.
 */
public class SymbolTable implements Cloneable {

  private final static int MAX_VALUE_NUMBER = Integer.MAX_VALUE / 4;

  /**
   * value numbers for parameters to this method
   */
  final private int[] parameters;

  /**
   * Mapping from Constant -> value number
   */
  private HashMap constants = HashMapFactory.make(10);

  private boolean copy = false;
  
  /**
   * @param numberOfParameters in the IR .. should be ir.getNumberOfParameters()
   */
    if (numberOfParameters < 0) {
      throw new IllegalArgumentException("Illegal numberOfParameters: " + numberOfParameters);
    }
    parameters = new int[numberOfParameters];
    for (int i = 0; i < parameters.length; i++) {
      parameters[i] = getNewValueNumber();
    }
  }

  /**
   * Values. Note: this class must maintain the following invariant: values.length > nextFreeValueNumber.
   */
  private Value[] values = new Value[5];

  private int nextFreeValueNumber = 1;

  /**
   * Method newSymbol.
   * 
   * @return int
   */
  public int newSymbol() {
    return getNewValueNumber();
  }

  /**
   * Common part of getConstant functions.
   * 
   * @param o instance of a Java 'boxed-primitive' class, String or NULL.
   * @return value number for constant.
   */
  int findOrCreateConstant(Object o) {
    ConstantValue v = new ConstantValue(o);
    Integer result = constants.get(v);
    if (result == null) {
      assert ! copy : "making value for " + o;
      int r = getNewValueNumber();
      result = Integer.valueOf(r);
      constants.put(v, result);
      assert r < nextFreeValueNumber;
      values[r] = v;
    } else {
      assert values[result.intValue()] instanceof ConstantValue;
    }
    return result.intValue();

  }

  public void setConstantValue(int vn, ConstantValue val) {
    try {
      assert vn < nextFreeValueNumber;
      values[vn] = val;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid vn: " + vn);
    }
  }

  private Object[] defaultValues;
  
  /**
   * Set the default value for a value number.  The notion of a default value
   * is for use by languages that do not require variables to be defined 
   * before they are used.  In this situation, SSA conversion can fail
   * since it depends on the assumption that values are always defined when
   * used.  The default value is the constant to be used in cases when a given
   * value is used without having been defined.  Currently, this is used only
   * by CAst front ends for languages with this "feature".
   */
  public void setDefaultValue(int vn, final Object defaultValue) {
      assert vn < nextFreeValueNumber;
 
      if (defaultValues == null) {
        defaultValues = new Object[vn*2 + 1];
      }
      
      if (defaultValues.length <= vn) {
        Object temp[] = defaultValues;
        defaultValues = new Object[ vn*2 + 1];
        System.arraycopy(temp, 0, defaultValues, 0, temp.length);
      }
      
      defaultValues[vn] = defaultValue;
   }

  public int getDefaultValue(int vn) {
    return findOrCreateConstant(defaultValues[vn]);
  }
  
  public int getNullConstant() {
    return findOrCreateConstant(null);
  }

  public int getConstant(boolean b) {
    return findOrCreateConstant(Boolean.valueOf(b));
  }

  public int getConstant(int i) {
    return findOrCreateConstant(Integer.valueOf(i));
  }

  public int getConstant(long l) {
    return findOrCreateConstant(Long.valueOf(l));
  }

  public int getConstant(float f) {
    return findOrCreateConstant(new Float(f));
  }
  public int getConstant(double d) {
    return findOrCreateConstant(new Double(d));
  }

  public int getConstant(String s) {
    return findOrCreateConstant(s);
  }

  /**
   * Return the value number of the ith parameter
   * 
   * By convention, for a non-static method, the 0th parameter is 'this'
   */
  public int getParameter(int i) throws IllegalArgumentException {
    try {
      return parameters[i];
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid i: " + i);
    }
  }

  private void expandForNewValueNumber(int vn) {
    if (vn >= values.length) {
      Value[] temp = values;
      values = new Value[2 * vn];
      System.arraycopy(temp, 0, values, 0, temp.length);
    }
  }

  private int getNewValueNumber() {
    int result = nextFreeValueNumber++;
    expandForNewValueNumber(result);
    return result;
  }

  /**
   * ensure that the symbol table has allocated space for the particular value number
   * 
   * @param i a value number
   */
  public void ensureSymbol(int i) {
    if (i < 0 || i > MAX_VALUE_NUMBER) {
      throw new IllegalArgumentException("Illegal i: " + i);
    }
    try {
      if (i != -1) {
        if (i >= values.length || values[i] == null) {
          if (nextFreeValueNumber <= i) {
            nextFreeValueNumber = i + 1;
          }
          expandForNewValueNumber(i);
        }
      }
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid i: " + i);
    }

  }

  public String getValueString(int valueNumber) {
    if (valueNumber < 0 || valueNumber > getMaxValueNumber() || values[valueNumber] == null
        || values[valueNumber] instanceof PhiValue) {
      return "v" + valueNumber;
    } else {
   * Method newSymbol.
      return "v" + valueNumber + ":" + values[valueNumber].toString();
    }
  }

  public boolean isConstant(int v) {
    try {
      return v < values.length && values[v] instanceof ConstantValue;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isZero(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isZeroConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isOne(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isOneConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isTrue(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isTrueConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isZeroOrFalse(int v) {
    return isZero(v) || isFalse(v);
  }

  public boolean isOneOrTrue(int v) {
    return isOne(v) || isTrue(v);
  }

  public boolean isFalse(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isFalseConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isBooleanOrZeroOneConstant(int v) {
    return isBooleanConstant(v) || isZero(v) || isOne(v);
  }

  public boolean isBooleanConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Boolean;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isIntegerConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && (((ConstantValue) values[v]).getValue() instanceof Integer);
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isLongConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && (((ConstantValue) values[v]).getValue() instanceof Long);
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isFloatConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Float;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isDoubleConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Double;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isNumberConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Number;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isStringConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof String;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isNullConstant(int v) {
    try {
      return (values.length > v) && (values[v] instanceof ConstantValue) && (((ConstantValue) values[v]).getValue() == null);
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  /**
   * @throws IllegalArgumentException if rhs is null
   */
  public int newPhi(int[] rhs) throws IllegalArgumentException {
    if (rhs == null) {
      throw new IllegalArgumentException("rhs is null");
    }
    int result = getNewValueNumber();
    SSAPhiInstruction phi = new SSAPhiInstruction(SSAInstruction.NO_INDEX, result, rhs.clone());
    assert result < nextFreeValueNumber;
    values[result] = new PhiValue(phi);
    return result;
  }

  /**
   * Return the PhiValue that is associated with a given value number
   */
  public PhiValue getPhiValue(int valueNumber) {
    try {
      return (PhiValue) values[valueNumber];
=======
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid valueNumber: " + valueNumber);
    } catch (ClassCastException e) {
      throw new IllegalArgumentException("invalid valueNumber: " + valueNumber);
    }
  }

  public int getMaxValueNumber() {
    // return values.length - 1;
    return nextFreeValueNumber - 1;
  }

  public int[] getParameterValueNumbers() {
    return parameters;
  }

  public int getNumberOfParameters() {
    return parameters.length;
  }

  public String getStringValue(int v) throws IllegalArgumentException {
    if (!isStringConstant(v)) {
      throw new IllegalArgumentException("not a string constant: value number " + v);
    }

    return (String) ((ConstantValue) values[v]).getValue();
  }

  public float getFloatValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).floatValue();
  }

  public double getDoubleValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).doubleValue();
  }

  public int getIntValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).intValue();
  }

  public long getLongValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).longValue();
  }

  public Object getConstantValue(int v) throws IllegalArgumentException {
    if (!isConstant(v)) {
  private int nextFreeValueNumber = 1;
      throw new IllegalArgumentException("value number " + v + " is not a constant.");
    }

    Object value = ((ConstantValue) values[v]).getValue();
    if (value == null) {
      return null;
    } else {
      return value;
    }
  }

  /**
   * @return the Value object for given value number or null if we have no special information about the value
   */
  public Value getValue(int valueNumber) {
    if (valueNumber < 1 || valueNumber >= values.length) {
      throw new IllegalArgumentException("Invalid value number " + valueNumber);
    }
    return values[valueNumber];
  }

  /**
   * @param valueNumber
   * @return true iff this valueNumber is a parameter
   */
  public boolean isParameter(int valueNumber) {
    return valueNumber <= getNumberOfParameters();
  }
  
  public SymbolTable copy() {
    try {
      SymbolTable nt = (SymbolTable) clone();
      nt.values = this.values.clone();
      if (this.defaultValues != null) {
        nt.defaultValues = this.defaultValues.clone();
      }
      nt.constants = HashMapFactory.make(this.constants);
      nt.copy = true;
      return nt;
    } catch (CloneNotSupportedException e) {
      Assertions.UNREACHABLE();
      return null;
    }
  }
}
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import java.util.HashMap;

import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.debug.Assertions;

/**
 * A symbol table which associates information with each variable (value number) in an SSA IR.
 * 
 * By convention, symbol numbers start at 1 ... the "this" parameter will be symbol number 1 in a virtual method.
 * 
 * This class is used heavily during SSA construction by {@link SSABuilder}.
 */
public class SymbolTable implements Cloneable {

  private final static int MAX_VALUE_NUMBER = Integer.MAX_VALUE / 4;

  /**
   * value numbers for parameters to this method
   */
  final private int[] parameters;

  /**
   * Mapping from Constant -> value number
   */
  private HashMap constants = HashMapFactory.make(10);

  private boolean copy = false;
  
  /**
   * @param numberOfParameters in the IR .. should be ir.getNumberOfParameters()
   */
  public SymbolTable(int numberOfParameters) {
    if (numberOfParameters < 0) {
      throw new IllegalArgumentException("Illegal numberOfParameters: " + numberOfParameters);
    }
    parameters = new int[numberOfParameters];
    for (int i = 0; i < parameters.length; i++) {
      parameters[i] = getNewValueNumber();
    }
  }

  /**
   * Values. Note: this class must maintain the following invariant: values.length > nextFreeValueNumber.
   */
  private Value[] values = new Value[5];


   * 
   * @return int
   */
  public int newSymbol() {
    return getNewValueNumber();
  }

  /**
   * Common part of getConstant functions.
   * 
   * @param o instance of a Java 'boxed-primitive' class, String or NULL.
   * @return value number for constant.
   */
  int findOrCreateConstant(Object o) {
    ConstantValue v = new ConstantValue(o);
    Integer result = constants.get(v);
    if (result == null) {
      assert ! copy : "making value for " + o;
      int r = getNewValueNumber();
      result = Integer.valueOf(r);
      constants.put(v, result);
      assert r < nextFreeValueNumber;
      values[r] = v;
    } else {
      assert values[result.intValue()] instanceof ConstantValue;
    }
    return result.intValue();

  }

  public void setConstantValue(int vn, ConstantValue val) {
    try {
      assert vn < nextFreeValueNumber;
      values[vn] = val;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid vn: " + vn);
    }
  }

  private Object[] defaultValues;
  
  /**
  public String getValueString(int valueNumber) {
   * Set the default value for a value number.  The notion of a default value
   * is for use by languages that do not require variables to be defined 
   * before they are used.  In this situation, SSA conversion can fail
   * since it depends on the assumption that values are always defined when
   * used.  The default value is the constant to be used in cases when a given
   * value is used without having been defined.  Currently, this is used only
   * by CAst front ends for languages with this "feature".
   */
  public void setDefaultValue(int vn, final Object defaultValue) {
      assert vn < nextFreeValueNumber;
 
      if (defaultValues == null) {
        defaultValues = new Object[vn*2 + 1];
      }
      
      if (defaultValues.length <= vn) {
        Object temp[] = defaultValues;
        defaultValues = new Object[ vn*2 + 1];
        System.arraycopy(temp, 0, defaultValues, 0, temp.length);
      }
      
      defaultValues[vn] = defaultValue;
   }

  public int getDefaultValue(int vn) {
    return findOrCreateConstant(defaultValues[vn]);
  }
  
  public int getNullConstant() {
    return findOrCreateConstant(null);
  }

  public int getConstant(boolean b) {
    return findOrCreateConstant(Boolean.valueOf(b));
  }

  public int getConstant(int i) {
    return findOrCreateConstant(Integer.valueOf(i));
  }

  public int getConstant(long l) {
    return findOrCreateConstant(Long.valueOf(l));
  }

  public int getConstant(float f) {
    return findOrCreateConstant(new Float(f));
  }

  public int getConstant(double d) {
    return findOrCreateConstant(new Double(d));
  }

  public int getConstant(String s) {
    return findOrCreateConstant(s);
  }

  /**
   * Return the value number of the ith parameter
   * 
   * By convention, for a non-static method, the 0th parameter is 'this'
   */
  public int getParameter(int i) throws IllegalArgumentException {
    try {
      return parameters[i];
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid i: " + i);
    }
  }

  private void expandForNewValueNumber(int vn) {
    if (vn >= values.length) {
      Value[] temp = values;
      values = new Value[2 * vn];
      System.arraycopy(temp, 0, values, 0, temp.length);
    }
  }

  private int getNewValueNumber() {
    int result = nextFreeValueNumber++;
    expandForNewValueNumber(result);
    return result;
  }

  /**
   * ensure that the symbol table has allocated space for the particular value number
   * 
   * @param i a value number
   */
  public void ensureSymbol(int i) {
    if (i < 0 || i > MAX_VALUE_NUMBER) {
      throw new IllegalArgumentException("Illegal i: " + i);
    }
    try {
      if (i != -1) {
        if (i >= values.length || values[i] == null) {
          if (nextFreeValueNumber <= i) {
            nextFreeValueNumber = i + 1;
          }
          expandForNewValueNumber(i);
        }
      }
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid i: " + i);
    }

  }

    if (valueNumber < 0 || valueNumber > getMaxValueNumber() || values[valueNumber] == null
        || values[valueNumber] instanceof PhiValue) {
      return "v" + valueNumber;
    } else {
      return "v" + valueNumber + ":" + values[valueNumber].toString();
    }
  }

  public boolean isConstant(int v) {
    try {
      return v < values.length && values[v] instanceof ConstantValue;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isZero(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isZeroConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isOne(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isOneConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isTrue(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isTrueConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isZeroOrFalse(int v) {
    return isZero(v) || isFalse(v);
  }

  public boolean isOneOrTrue(int v) {
    return isOne(v) || isTrue(v);
  }

  public boolean isFalse(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isFalseConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isBooleanOrZeroOneConstant(int v) {
    return isBooleanConstant(v) || isZero(v) || isOne(v);
  }

  public boolean isBooleanConstant(int v) {
    try {
    return parameters.length;
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Boolean;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isIntegerConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && (((ConstantValue) values[v]).getValue() instanceof Integer);
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isLongConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && (((ConstantValue) values[v]).getValue() instanceof Long);
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isFloatConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Float;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isDoubleConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Double;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isNumberConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Number;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isStringConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof String;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isNullConstant(int v) {
    try {
      return (values.length > v) && (values[v] instanceof ConstantValue) && (((ConstantValue) values[v]).getValue() == null);
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  /**
   * @throws IllegalArgumentException if rhs is null
   */
  public int newPhi(int[] rhs) throws IllegalArgumentException {
    if (rhs == null) {
      throw new IllegalArgumentException("rhs is null");
    }
    int result = getNewValueNumber();
    SSAPhiInstruction phi = new SSAPhiInstruction(result, rhs.clone());
    assert result < nextFreeValueNumber;
    values[result] = new PhiValue(phi);
    return result;
  }

  /**
   * Return the PhiValue that is associated with a given value number
   */
  public PhiValue getPhiValue(int valueNumber) {
    try {
      return (PhiValue) values[valueNumber];
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid valueNumber: " + valueNumber);
    } catch (ClassCastException e) {
      throw new IllegalArgumentException("invalid valueNumber: " + valueNumber);
    }
  }

  public int getMaxValueNumber() {
    // return values.length - 1;
    return nextFreeValueNumber - 1;
  }

  public int[] getParameterValueNumbers() {
    return parameters;
  }

  public int getNumberOfParameters() {
  }

  public String getStringValue(int v) throws IllegalArgumentException {
    if (!isStringConstant(v)) {
      throw new IllegalArgumentException("not a string constant: value number " + v);
    }

    return (String) ((ConstantValue) values[v]).getValue();
  }

  public float getFloatValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).floatValue();
  }

  public double getDoubleValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).doubleValue();
  }

  public int getIntValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).intValue();
  }

  public long getLongValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).longValue();
  }

  public Object getConstantValue(int v) throws IllegalArgumentException {
    if (!isConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a constant.");
    }

    Object value = ((ConstantValue) values[v]).getValue();
    if (value == null) {
      return null;
    } else {
      return value;
    }
  }

  /**
   * @return the Value object for given value number or null if we have no special information about the value
   */
  public Value getValue(int valueNumber) {
    if (valueNumber < 1 || valueNumber >= values.length) {
      throw new IllegalArgumentException("Invalid value number " + valueNumber);
    }
    return values[valueNumber];
  }

  /**
   * @param valueNumber
   * @return true iff this valueNumber is a parameter
   */
  public boolean isParameter(int valueNumber) {
    return valueNumber <= getNumberOfParameters();
  }
  
  public SymbolTable copy() {
    try {
      SymbolTable nt = (SymbolTable) clone();
      nt.values = this.values.clone();
      if (this.defaultValues != null) {
        nt.defaultValues = this.defaultValues.clone();
      }
      nt.constants = HashMapFactory.make(this.constants);
      nt.copy = true;
      return nt;
    } catch (CloneNotSupportedException e) {
      Assertions.UNREACHABLE();
      return null;
    }
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
    }
    }
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa;

import java.util.HashMap;

import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.debug.Assertions;

/**
 * A symbol table which associates information with each variable (value number) in an SSA IR.
 * 
 * By convention, symbol numbers start at 1 ... the "this" parameter will be symbol number 1 in a virtual method.
 * 
 * This class is used heavily during SSA construction by {@link SSABuilder}.
 */
public class SymbolTable implements Cloneable {

  private final static int MAX_VALUE_NUMBER = Integer.MAX_VALUE / 4;

  /**
   * value numbers for parameters to this method
   */
  final private int[] parameters;

  /**
   * Mapping from Constant -> value number
   */
  private HashMap constants = HashMapFactory.make(10);

  private boolean copy = false;
  
  /**
   * @param numberOfParameters in the IR .. should be ir.getNumberOfParameters()
   */
  public SymbolTable(int numberOfParameters) {
    if (numberOfParameters < 0) {
      throw new IllegalArgumentException("Illegal numberOfParameters: " + numberOfParameters);
    }
    parameters = new int[numberOfParameters];
    for (int i = 0; i < parameters.length; i++) {
      parameters[i] = getNewValueNumber();
    }
  }

  /**
   * Values. Note: this class must maintain the following invariant: values.length > nextFreeValueNumber.
   */
  private Value[] values = new Value[5];

  private int nextFreeValueNumber = 1;

  /**
   * Method newSymbol.
   * 
   * @return int
   */
  public int newSymbol() {
    return getNewValueNumber();
  }

  /**
   * Common part of getConstant functions.
   * 
   * @param o instance of a Java 'boxed-primitive' class, String or NULL.
   * @return value number for constant.
   */
  int findOrCreateConstant(Object o) {
    ConstantValue v = new ConstantValue(o);
    Integer result = constants.get(v);
    if (result == null) {
      assert ! copy : "making value for " + o;
      int r = getNewValueNumber();
      result = Integer.valueOf(r);
      constants.put(v, result);
      assert r < nextFreeValueNumber;
      values[r] = v;
    } else {
      assert values[result.intValue()] instanceof ConstantValue;
    return result.intValue();

  }

  public void setConstantValue(int vn, ConstantValue val) {
    try {
      assert vn < nextFreeValueNumber;
      values[vn] = val;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid vn: " + vn);
    }
  }

  private Object[] defaultValues;
  
  /**

   * Set the default value for a value number.  The notion of a default value
   * is for use by languages that do not require variables to be defined 
   * before they are used.  In this situation, SSA conversion can fail
   * since it depends on the assumption that values are always defined when
   * used.  The default value is the constant to be used in cases when a given
   * value is used without having been defined.  Currently, this is used only
   * by CAst front ends for languages with this "feature".
   */
  public void setDefaultValue(int vn, final Object defaultValue) {
      assert vn < nextFreeValueNumber;
 
      if (defaultValues == null) {
        defaultValues = new Object[vn*2 + 1];
      }
      
      if (defaultValues.length <= vn) {
        Object temp[] = defaultValues;
        defaultValues = new Object[ vn*2 + 1];
        System.arraycopy(temp, 0, defaultValues, 0, temp.length);
      }
      
      defaultValues[vn] = defaultValue;
   }

  public int getDefaultValue(int vn) {
    return findOrCreateConstant(defaultValues[vn]);
  }
  
  public int getNullConstant() {
    return findOrCreateConstant(null);
  }

  public int getConstant(boolean b) {
    return findOrCreateConstant(Boolean.valueOf(b));
  }

  public int getConstant(int i) {
    return findOrCreateConstant(Integer.valueOf(i));
  }

  public int getConstant(long l) {
    return findOrCreateConstant(Long.valueOf(l));
  }

  public int getConstant(float f) {
    return findOrCreateConstant(new Float(f));
  }

  public int getConstant(double d) {
    return findOrCreateConstant(new Double(d));
  }

  public int getConstant(String s) {
    return findOrCreateConstant(s);
  }

  /**
   * Return the value number of the ith parameter
   * 
   * By convention, for a non-static method, the 0th parameter is 'this'
   */
  public int getParameter(int i) throws IllegalArgumentException {
    try {
      return parameters[i];
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid i: " + i);
    }
  }

  private void expandForNewValueNumber(int vn) {
    if (vn >= values.length) {
      Value[] temp = values;
      values = new Value[2 * vn];
      System.arraycopy(temp, 0, values, 0, temp.length);
    }
  }

  private int getNewValueNumber() {
    int result = nextFreeValueNumber++;
    expandForNewValueNumber(result);
    return result;
  }

  /**
   * ensure that the symbol table has allocated space for the particular value number
   * 
   * @param i a value number
   */
  public void ensureSymbol(int i) {
    if (i < 0 || i > MAX_VALUE_NUMBER) {
      throw new IllegalArgumentException("Illegal i: " + i);
    try {
      if (i != -1) {
        if (i >= values.length || values[i] == null) {
          if (nextFreeValueNumber <= i) {
            nextFreeValueNumber = i + 1;
          }
          expandForNewValueNumber(i);
        }
      }
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid i: " + i);
    }

  }

  public String getValueString(int valueNumber) {
    if (valueNumber < 0 || valueNumber > getMaxValueNumber() || values[valueNumber] == null
        || values[valueNumber] instanceof PhiValue) {
      return "v" + valueNumber;
    } else {
      return "v" + valueNumber + ":" + values[valueNumber].toString();
    }
  }

  public boolean isConstant(int v) {
    try {
      return v < values.length && values[v] instanceof ConstantValue;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isZero(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isZeroConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isOne(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isOneConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isTrue(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isTrueConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isZeroOrFalse(int v) {
    return isZero(v) || isFalse(v);
  }

  public boolean isOneOrTrue(int v) {
    return isOne(v) || isTrue(v);
  }

  public boolean isFalse(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).isFalseConstant();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isBooleanOrZeroOneConstant(int v) {
    return isBooleanConstant(v) || isZero(v) || isOne(v);
  }

  public boolean isBooleanConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Boolean;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isIntegerConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && (((ConstantValue) values[v]).getValue() instanceof Integer);
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isLongConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && (((ConstantValue) values[v]).getValue() instanceof Long);
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isFloatConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Float;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isDoubleConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Double;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }
  public boolean isNumberConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof Number;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isStringConstant(int v) {
    try {
      return (values[v] instanceof ConstantValue) && ((ConstantValue) values[v]).getValue() instanceof String;
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  public boolean isNullConstant(int v) {
    try {
      return (values.length > v) && (values[v] instanceof ConstantValue) && (((ConstantValue) values[v]).getValue() == null);
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid v: " + v);
    }
  }

  /**
   * @throws IllegalArgumentException if rhs is null
   */
  public int newPhi(int[] rhs) throws IllegalArgumentException {
    if (rhs == null) {
      throw new IllegalArgumentException("rhs is null");
    }
    int result = getNewValueNumber();
    SSAPhiInstruction phi = new SSAPhiInstruction(SSAInstruction.NO_INDEX, result, rhs.clone());
    assert result < nextFreeValueNumber;
    values[result] = new PhiValue(phi);
    return result;
  }

  /**
   * Return the PhiValue that is associated with a given value number
   */
  public PhiValue getPhiValue(int valueNumber) {
    try {
      return (PhiValue) values[valueNumber];
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new IllegalArgumentException("invalid valueNumber: " + valueNumber);
    } catch (ClassCastException e) {
      throw new IllegalArgumentException("invalid valueNumber: " + valueNumber);
    }
  }

  public int getMaxValueNumber() {
    // return values.length - 1;
    return nextFreeValueNumber - 1;
  }

  public int[] getParameterValueNumbers() {
    return parameters;
  }

  public int getNumberOfParameters() {
    return parameters.length;
  }

  public String getStringValue(int v) throws IllegalArgumentException {
    if (!isStringConstant(v)) {
      throw new IllegalArgumentException("not a string constant: value number " + v);
    }

    return (String) ((ConstantValue) values[v]).getValue();
  }

  public float getFloatValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).floatValue();
  }

  public double getDoubleValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).doubleValue();
  }

  public int getIntValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).intValue();
  }

  public long getLongValue(int v) throws IllegalArgumentException {
    if (!isNumberConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a numeric constant.");
    }
    return ((Number) ((ConstantValue) values[v]).getValue()).longValue();
  }

  public Object getConstantValue(int v) throws IllegalArgumentException {
    if (!isConstant(v)) {
      throw new IllegalArgumentException("value number " + v + " is not a constant.");
    }

    Object value = ((ConstantValue) values[v]).getValue();
    if (value == null) {
      return null;
    } else {
      return value;
    }
  }

  /**
   * @return the Value object for given value number or null if we have no special information about the value
   */
  public Value getValue(int valueNumber) {
    if (valueNumber < 1 || valueNumber >= values.length) {
      throw new IllegalArgumentException("Invalid value number " + valueNumber);
    }
    return values[valueNumber];
  }

  /**
   * @param valueNumber
   * @return true iff this valueNumber is a parameter
   */
  public boolean isParameter(int valueNumber) {
    return valueNumber <= getNumberOfParameters();
  }
  
  public SymbolTable copy() {
    try {
      SymbolTable nt = (SymbolTable) clone();
      nt.values = this.values.clone();
      if (this.defaultValues != null) {
        nt.defaultValues = this.defaultValues.clone();
      }
      nt.constants = HashMapFactory.make(this.constants);
      nt.copy = true;
      return nt;
    } catch (CloneNotSupportedException e) {
      Assertions.UNREACHABLE();
      return null;
    }
  }
}
File
SymbolTable.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa.analysis;

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

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSACFG.ExceptionHandlerBasicBlock;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.collections.SimpleVector;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.intset.BitVector;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.MutableSparseIntSet;

/**
 * A view of a control flow graph where each basic block corresponds to exactly one SSA instruction index.
 * 
 * Prototype: Not terribly efficient.
 */
public class ExplodedControlFlowGraph implements ControlFlowGraph {

  private final static int ENTRY_INDEX = -1;

      } else {
  private final static int EXIT_INDEX = -2;

  private final IR ir;

  /**
   * The ith element of this vector is the basic block holding instruction i. this basic block has number i+1.
   */
  private final SimpleVector normalNodes = new SimpleVector();

  private final Collection allNodes = HashSetFactory.make();

  private final IExplodedBasicBlock entry;

  private final IExplodedBasicBlock exit;

  private ExplodedControlFlowGraph(IR ir) {
    assert ir != null;
    this.ir = ir;
    this.entry = new ExplodedBasicBlock(ENTRY_INDEX, null);
    this.exit = new ExplodedBasicBlock(EXIT_INDEX, null);
    createNodes();
  }

  private void createNodes() {
    allNodes.add(entry);
    allNodes.add(exit);
    for (ISSABasicBlock b : ir.getControlFlowGraph()) {
      for (int i = b.getFirstInstructionIndex(); i <= b.getLastInstructionIndex(); i++) {
        IExplodedBasicBlock bb = new ExplodedBasicBlock(i, b);
        normalNodes.set(i, bb);
        allNodes.add(bb);
      }
    }
  }

  public static ExplodedControlFlowGraph make(IR ir) {
    if (ir == null) {
      throw new IllegalArgumentException("ir == null");
    }
    return new ExplodedControlFlowGraph(ir);
  }

  public IExplodedBasicBlock entry() {
    return entry;
  }

  public IExplodedBasicBlock exit() {
    return exit;
  }

  public IExplodedBasicBlock getBlockForInstruction(int index) {
    return normalNodes.get(index);
  }

  /*
   * @see com.ibm.wala.cfg.ControlFlowGraph#getCatchBlocks()
   */
  public BitVector getCatchBlocks() {
    BitVector original = ir.getControlFlowGraph().getCatchBlocks();
    BitVector result = new BitVector();
    for (int i = 0; i <= original.max(); i++) {
      if (original.get(i)) {
        result.set(i + 1);
      }
    }
    return result;
  }

  public Collection getExceptionalPredecessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.equals(entry)) {
      return Collections.emptySet();
    }
    if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      List result = new ArrayList();
      for (ISSABasicBlock s : ir.getControlFlowGraph().getExceptionalPredecessors(eb.original)) {
        assert normalNodes.get(s.getLastInstructionIndex()) != null;
        result.add(normalNodes.get(s.getLastInstructionIndex()));
      }
      return result;
    } else {
      return Collections.emptySet();
    }
  }

  public List getExceptionalSuccessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;    
    assert eb != null;
    if (eb.equals(exit)) {
      return Collections.emptyList();
    }
    if (eb.isEntryBlock() || eb.instructionIndex == eb.original.getLastInstructionIndex()) {
      List result = new ArrayList();
/** BEGIN Custom change: fix */
      ISSABasicBlock orig = eb.original;
      if (eb.isEntryBlock() && orig == null) {
        orig = ir.getControlFlowGraph().entry();
      }
      
      for (ISSABasicBlock s : ir.getControlFlowGraph().getExceptionalSuccessors(orig)) {
/** END Custom change: fix */
        if (s.equals(ir.getControlFlowGraph().exit())) {
          result.add(exit());
        } else {
          assert normalNodes.get(s.getFirstInstructionIndex()) != null;
          result.add(normalNodes.get(s.getFirstInstructionIndex()));
        }
      }
      return result;
    } else {
      return Collections.emptyList();
    }
  }

  public SSAInstruction[] getInstructions() {
    return ir.getInstructions();
  }

  public IMethod getMethod() throws UnimplementedError {
    return ir.getMethod();
  }

  public Collection getNormalPredecessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.equals(entry)) {
      return Collections.emptySet();
    }
    if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      List result = new ArrayList();
      for (ISSABasicBlock s : ir.getControlFlowGraph().getNormalPredecessors(eb.original)) {
        if (s.equals(ir.getControlFlowGraph().entry())) {
          if (s.getLastInstructionIndex() >= 0) {
            assert normalNodes.get(s.getLastInstructionIndex()) != null;
            result.add(normalNodes.get(s.getLastInstructionIndex()));
          } else {
            result.add(entry());
          }
        } else {
          assert normalNodes.get(s.getLastInstructionIndex()) != null;
          result.add(normalNodes.get(s.getLastInstructionIndex()));
        }
      }
      return result;
    } else {
      assert normalNodes.get(eb.instructionIndex - 1) != null;
      return Collections.singleton(normalNodes.get(eb.instructionIndex - 1));
    }
  }

  public Collection getNormalSuccessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.equals(exit)) {
      return Collections.emptySet();
    }
    if (eb.isEntryBlock()) {
      return Collections.singleton(normalNodes.get(0));
    }
    if (eb.instructionIndex == eb.original.getLastInstructionIndex()) {
      List result = new ArrayList();
      for (ISSABasicBlock s : ir.getControlFlowGraph().getNormalSuccessors(eb.original)) {
        if (s.equals(ir.getControlFlowGraph().exit())) {
          result.add(exit());
        } else {
          assert normalNodes.get(s.getFirstInstructionIndex()) != null;
          result.add(normalNodes.get(s.getFirstInstructionIndex()));
        }
      }
      return result;
    } else {
      assert normalNodes.get(eb.instructionIndex + 1) != null;
      return Collections.singleton(normalNodes.get(eb.instructionIndex + 1));
    }
  }

  /*
   * @see com.ibm.wala.cfg.ControlFlowGraph#getProgramCounter(int)
   */
  public int getProgramCounter(int index) throws UnimplementedError {
    return ir.getControlFlowGraph().getProgramCounter(index);
  }

  public void removeNodeAndEdges(IExplodedBasicBlock N) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public void addNode(IExplodedBasicBlock n) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public boolean containsNode(IExplodedBasicBlock N) {
    return allNodes.contains(N);
  }

  public int getNumberOfNodes() {
    return allNodes.size();
  }

  public Iterator iterator() {
    return allNodes.iterator();
  }

  public void removeNode(IExplodedBasicBlock n) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public void addEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public int getPredNodeCount(IExplodedBasicBlock bb) throws IllegalArgumentException {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;    
    if (eb == null) {
      throw new IllegalArgumentException("eb == null");
    }
    if (eb.isEntryBlock()) {
      return 0;
    }
    if (eb.isExitBlock()) {
      return ir.getControlFlowGraph().getPredNodeCount(ir.getControlFlowGraph().exit());
    }
    if (eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      if (eb.original.isEntryBlock()) {
        return 1;
      } else {
        return ir.getControlFlowGraph().getPredNodeCount(eb.original);
      }
    } else {
      return 1;
    }
  }

  public Iterator getPredNodes(IExplodedBasicBlock bb) throws IllegalArgumentException {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    if (eb == null) {
      throw new IllegalArgumentException("eb == null");
    }
    if (eb.isEntryBlock()) {
      return EmptyIterator.instance();
    }
    ISSABasicBlock original = eb.isExitBlock() ? ir.getControlFlowGraph().exit() : eb.original;
    if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      List result = new ArrayList();
      if (eb.original != null && eb.original.isEntryBlock()) {
        result.add(entry);
      }
      for (Iterator it = ir.getControlFlowGraph().getPredNodes(original); it.hasNext();) {
        ISSABasicBlock s = it.next();
        if (s.isEntryBlock()) {
          // it's possible for an entry block to have instructions; in this case, add
          // the exploded basic block for the last instruction in the entry block
          if (s.getLastInstructionIndex() >= 0) {
            result.add(normalNodes.get(s.getLastInstructionIndex()));
          } else {
            result.add(entry);
          }
        } else {
          assert normalNodes.get(s.getLastInstructionIndex()) != null;
          result.add(normalNodes.get(s.getLastInstructionIndex()));
        }
      }
      return result.iterator();
    } else {
      assert normalNodes.get(eb.instructionIndex - 1) != null;
      return NonNullSingletonIterator.make(normalNodes.get(eb.instructionIndex - 1));
    }
  }

  public int getSuccNodeCount(IExplodedBasicBlock N) throws UnimplementedError {
    return Iterator2Collection.toSet(getSuccNodes(N)).size();
  }

  /*
   * @see com.ibm.wala.util.graph.EdgeManager#getSuccNodes(java.lang.Object)
   */
  public Iterator getSuccNodes(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.isExitBlock()) {
      return EmptyIterator.instance();
    }
    if (eb.isEntryBlock()) {
      IExplodedBasicBlock z = normalNodes.get(0);
      return z == null ? EmptyIterator. instance() : NonNullSingletonIterator.make(z);
    }
    if (eb.instructionIndex == eb.original.getLastInstructionIndex()) {
      List result = new ArrayList();
      for (Iterator it = ir.getControlFlowGraph().getSuccNodes(eb.original); it.hasNext();) {
        ISSABasicBlock s = it.next();
        if (s.equals(ir.getControlFlowGraph().exit())) {
          result.add(exit());
        } else {
          // there can be a weird corner case where a void method without a
          // return statement
          // can have trailing empty basic blocks with no instructions. ignore
          // these.
          if (normalNodes.get(s.getFirstInstructionIndex()) != null) {
            result.add(normalNodes.get(s.getFirstInstructionIndex()));
          }
        }
      }
      return result.iterator();
    } else {
      assert normalNodes.get(eb.instructionIndex + 1) != null;
      return NonNullSingletonIterator.make(normalNodes.get(eb.instructionIndex + 1));
    }
  }

  public boolean hasEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnimplementedError {
/** BEGIN Custom change: implement hasEdge */
    for (Iterator it = getSuccNodes(src); it.hasNext();) {
      IExplodedBasicBlock succ = it.next();
      if (succ == dst) {
        return true;
      }
    }
    
    return false;
/** END Custom change: implement hasEdge */
  }

  public void removeAllIncidentEdges(IExplodedBasicBlock node) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();

  }

  public void removeEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public void removeIncomingEdges(IExplodedBasicBlock node) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public void removeOutgoingEdges(IExplodedBasicBlock node) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public int getMaxNumber() {
    return getNumberOfNodes() - 1;
  }

  public IExplodedBasicBlock getNode(int number) {
    if (number == 0) {
      return entry();
    } else if (number == getNumberOfNodes() - 1) {
      return exit();
    } else {
      return normalNodes.get(number - 1);
    }
  }

  public int getNumber(IExplodedBasicBlock n) throws IllegalArgumentException {
    if (n == null) {
    return null;
      throw new IllegalArgumentException("n == null");
    }
    return n.getNumber();
  }

  public Iterator iterateNodes(IntSet s) throws UnimplementedError {
    Assertions.UNREACHABLE();
    return null;
  }

  public IntSet getPredNodeNumbers(IExplodedBasicBlock node) {
    MutableSparseIntSet result = MutableSparseIntSet.makeEmpty();
    for (Iterator it = getPredNodes(node); it.hasNext();) {
      result.add(getNumber(it.next()));
    }
    return result;
  }

  public IntSet getSuccNodeNumbers(IExplodedBasicBlock node) throws UnimplementedError {
    Assertions.UNREACHABLE();
    return null;
  }

  /**
   * A basic block with exactly one normal instruction (which may be null), corresponding to a single instruction index in the SSA
   * instruction array.
   * 
   * The block may also have phis.
   */
  private class ExplodedBasicBlock implements IExplodedBasicBlock {

    private final int instructionIndex;

    private final ISSABasicBlock original;

    public ExplodedBasicBlock(int instructionIndex, ISSABasicBlock original) {
      this.instructionIndex = instructionIndex;
      this.original = original;
    }

    @SuppressWarnings("unused")
    public ExplodedControlFlowGraph getExplodedCFG() {
      return ExplodedControlFlowGraph.this;
    }

    public Iterator getCaughtExceptionTypes() {
      if (original instanceof ExceptionHandlerBasicBlock) {
        ExceptionHandlerBasicBlock eb = (ExceptionHandlerBasicBlock) original;
        return eb.getCaughtExceptionTypes();
      } else {
        return EmptyIterator.instance();
      }
    }

    public int getFirstInstructionIndex() {
      return instructionIndex;
    }

    public int getLastInstructionIndex() {
      return instructionIndex;
    }

    public IMethod getMethod() {
      return ExplodedControlFlowGraph.this.getMethod();
    }

    public int getNumber() {
      if (isEntryBlock()) {
        return 0;
      } else if (isExitBlock()) {
        return getNumberOfNodes() - 1;
      } else {
        return instructionIndex + 1;
      }
    }

    public boolean isCatchBlock() {
      if (original == null) {
        return false;
      }
      return (original.isCatchBlock() && instructionIndex == original.getFirstInstructionIndex());
    }

    public SSAGetCaughtExceptionInstruction getCatchInstruction() {
      if (!(original instanceof ExceptionHandlerBasicBlock)) {
        throw new IllegalArgumentException("block does not represent an exception handler");
      }
      ExceptionHandlerBasicBlock e = (ExceptionHandlerBasicBlock) original;
      return e.getCatchInstruction();
    }

    public boolean isEntryBlock() {
      return instructionIndex == ENTRY_INDEX;
    }

    public boolean isExitBlock() {
      return instructionIndex == EXIT_INDEX;
    }

    public int getGraphNodeId() {
      return getNumber();
    }

    public void setGraphNodeId(int number) {
      Assertions.UNREACHABLE();

    }

    public Iterator iterator() {
      if (isEntryBlock() || isExitBlock() || getInstruction() == null) {
        return EmptyIterator.instance();
      } else {
        return NonNullSingletonIterator.make(getInstruction());
      }
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + instructionIndex;
      result = prime * result + ((original == null) ? 0 : original.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      final ExplodedBasicBlock other = (ExplodedBasicBlock) obj;
      if (instructionIndex != other.instructionIndex)
        return false;
      if (original == null) {
        if (other.original != null)
          return false;
      } else if (!original.equals(other.original))
        return false;
      return true;
    }

    public SSAInstruction getInstruction() {
      if (isEntryBlock() || isExitBlock()) {
        return null;
      } else {
        return ir.getInstructions()[instructionIndex];
      }
    }

    public SSAInstruction getLastInstruction() {
      if (getLastInstructionIndex() < 0) {
        return null;
      } else {
        return ir.getInstructions()[getLastInstructionIndex()];
      }
    }

    public Iterator iteratePhis() {
      if (isEntryBlock() || isExitBlock() || instructionIndex != original.getFirstInstructionIndex()) {
        return EmptyIterator.instance();
      } else {
        return original.iteratePhis();
      }
    }

    public Iterator iteratePis() {
      if (isEntryBlock() || isExitBlock() || instructionIndex != original.getLastInstructionIndex()) {
        return EmptyIterator.instance();
      } else {
        return original.iteratePis();
      }
    }

    @Override
    public String toString() {
      if (isEntryBlock()) {
        return "ExplodedBlock[" + getNumber() + "](entry:" + getMethod() + ")";
      }
      if (isExitBlock()) {
        return "ExplodedBlock[" + getNumber() + "](exit:" + getMethod() + ")";
      }
      return "ExplodedBlock[" + getNumber() + "](original:" + original + ")";
    }
  }

  @Override
  public String toString() {
    StringBuffer s = new StringBuffer("");
    for (Iterator it = iterator(); it.hasNext();) {
      IExplodedBasicBlock bb = it.next();
      s.append("BB").append(getNumber(bb)).append("\n");

      Iterator succNodes = getSuccNodes(bb);
      while (succNodes.hasNext()) {
        s.append("    -> BB").append(getNumber(succNodes.next())).append("\n");
      }
    }
    return s.toString();
  }

  public IR getIR() {
    return ir;
  }

=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa.analysis;

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

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSACFG.ExceptionHandlerBasicBlock;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.collections.SimpleVector;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.intset.BitVector;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.MutableSparseIntSet;

/**
 * A view of a control flow graph where each basic block corresponds to exactly one SSA instruction index.
 * 
 * Prototype: Not terribly efficient.
 */
public class ExplodedControlFlowGraph implements ControlFlowGraph {

  private final static int ENTRY_INDEX = -1;

  private final static int EXIT_INDEX = -2;

  private final IR ir;

  /**
   * The ith element of this vector is the basic block holding instruction i. this basic block has number i+1.
   */
  private final SimpleVector normalNodes = new SimpleVector();

  private final Collection allNodes = HashSetFactory.make();

  private final IExplodedBasicBlock entry;

  private final IExplodedBasicBlock exit;

  private ExplodedControlFlowGraph(IR ir) {
    assert ir != null;
    this.ir = ir;
    this.entry = new ExplodedBasicBlock(ENTRY_INDEX, null);
    this.exit = new ExplodedBasicBlock(EXIT_INDEX, null);
    createNodes();
  }

  private void createNodes() {
    allNodes.add(entry);
    allNodes.add(exit);
    for (ISSABasicBlock b : ir.getControlFlowGraph()) {
      for (int i = b.getFirstInstructionIndex(); i <= b.getLastInstructionIndex(); i++) {
        IExplodedBasicBlock bb = new ExplodedBasicBlock(i, b);
        normalNodes.set(i, bb);
        allNodes.add(bb);
      }
    }
  }

  public static ExplodedControlFlowGraph make(IR ir) {
    if (ir == null) {
      throw new IllegalArgumentException("ir == null");
    }
    return new ExplodedControlFlowGraph(ir);
  }

  public IExplodedBasicBlock entry() {
    return entry;
  }

  public IExplodedBasicBlock exit() {
    return exit;
  }

  public IExplodedBasicBlock getBlockForInstruction(int index) {
    return normalNodes.get(index);
  }

  /*
   * @see com.ibm.wala.cfg.ControlFlowGraph#getCatchBlocks()
   */
  public BitVector getCatchBlocks() {
    BitVector original = ir.getControlFlowGraph().getCatchBlocks();
    BitVector result = new BitVector();
    for (int i = 0; i <= original.max(); i++) {
      if (original.get(i)) {
        result.set(i + 1);
      }
    }
    return result;
  }

  public Collection getExceptionalPredecessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.equals(entry)) {
      return Collections.emptySet();
    }
    if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      List result = new ArrayList();
      for (ISSABasicBlock s : ir.getControlFlowGraph().getExceptionalPredecessors(eb.original)) {
        assert normalNodes.get(s.getLastInstructionIndex()) != null;
        result.add(normalNodes.get(s.getLastInstructionIndex()));
      }
      return result;
    } else {
      return Collections.emptySet();
    }
  }

  public List getExceptionalSuccessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;    
    assert eb != null;
    if (eb.equals(exit)) {
      return Collections.emptyList();
    }
    if (eb.isEntryBlock() || eb.instructionIndex == eb.original.getLastInstructionIndex()) {
      List result = new ArrayList();
      for (ISSABasicBlock s : ir.getControlFlowGraph().getExceptionalSuccessors(eb.original)) {
        if (s.equals(ir.getControlFlowGraph().exit())) {
          result.add(exit());
        } else {
          assert normalNodes.get(s.getFirstInstructionIndex()) != null;
          result.add(normalNodes.get(s.getFirstInstructionIndex()));
        }
      }
      return result;
    } else {
      return Collections.emptyList();
    }
  }

  public SSAInstruction[] getInstructions() {
    return ir.getInstructions();
  }

  public IMethod getMethod() throws UnimplementedError {
    return ir.getMethod();
  }

  public Collection getNormalPredecessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.equals(entry)) {
      return Collections.emptySet();
    }
    if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      List result = new ArrayList();
      for (ISSABasicBlock s : ir.getControlFlowGraph().getNormalPredecessors(eb.original)) {
        if (s.equals(ir.getControlFlowGraph().entry())) {
          if (s.getLastInstructionIndex() >= 0) {
            assert normalNodes.get(s.getLastInstructionIndex()) != null;
            result.add(normalNodes.get(s.getLastInstructionIndex()));
          } else {
            result.add(entry());
          }
        } else {
          assert normalNodes.get(s.getLastInstructionIndex()) != null;
          result.add(normalNodes.get(s.getLastInstructionIndex()));
        }
      }
      return result;
    } else {
      assert normalNodes.get(eb.instructionIndex - 1) != null;
      return Collections.singleton(normalNodes.get(eb.instructionIndex - 1));
    }
  }

  public Collection getNormalSuccessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.equals(exit)) {
      return Collections.emptySet();
    }
    if (eb.isEntryBlock()) {
      return Collections.singleton(normalNodes.get(0));
    }
  }
    if (eb.instructionIndex == eb.original.getLastInstructionIndex()) {
      List result = new ArrayList();
      for (ISSABasicBlock s : ir.getControlFlowGraph().getNormalSuccessors(eb.original)) {
        if (s.equals(ir.getControlFlowGraph().exit())) {
          result.add(exit());
        } else {
          assert normalNodes.get(s.getFirstInstructionIndex()) != null;
          result.add(normalNodes.get(s.getFirstInstructionIndex()));
        }
      }
      return result;
    } else {
      assert normalNodes.get(eb.instructionIndex + 1) != null;
      return Collections.singleton(normalNodes.get(eb.instructionIndex + 1));
    }
  }

  /*
   * @see com.ibm.wala.cfg.ControlFlowGraph#getProgramCounter(int)
   */
  public int getProgramCounter(int index) throws UnimplementedError {
    return ir.getControlFlowGraph().getProgramCounter(index);
  }

  public void removeNodeAndEdges(IExplodedBasicBlock N) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public void addNode(IExplodedBasicBlock n) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public boolean containsNode(IExplodedBasicBlock N) {
    return allNodes.contains(N);
  }

  public int getNumberOfNodes() {
    return allNodes.size();
  }

  public Iterator iterator() {
    return allNodes.iterator();
  }

  public void removeNode(IExplodedBasicBlock n) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public void addEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public int getPredNodeCount(IExplodedBasicBlock bb) throws IllegalArgumentException {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;    
    if (eb == null) {
      throw new IllegalArgumentException("eb == null");
    }
    if (eb.isEntryBlock()) {
      return 0;
    }
    if (eb.isExitBlock()) {
      return ir.getControlFlowGraph().getPredNodeCount(ir.getControlFlowGraph().exit());
    }
    if (eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      if (eb.original.isEntryBlock()) {
        return 1;
      } else {
        return ir.getControlFlowGraph().getPredNodeCount(eb.original);
      }
    } else {
      return 1;
    }
  }

  public Iterator getPredNodes(IExplodedBasicBlock bb) throws IllegalArgumentException {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    if (eb == null) {
      throw new IllegalArgumentException("eb == null");
    }
    if (eb.isEntryBlock()) {
      return EmptyIterator.instance();
    }
    ISSABasicBlock original = eb.isExitBlock() ? ir.getControlFlowGraph().exit() : eb.original;
    if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      List result = new ArrayList();
      if (eb.original != null && eb.original.isEntryBlock()) {
        result.add(entry);
      }
      for (Iterator it = ir.getControlFlowGraph().getPredNodes(original); it.hasNext();) {
        ISSABasicBlock s = it.next();
        if (s.isEntryBlock()) {
          // it's possible for an entry block to have instructions; in this case, add
          // the exploded basic block for the last instruction in the entry block
          if (s.getLastInstructionIndex() >= 0) {
            result.add(normalNodes.get(s.getLastInstructionIndex()));
          } else {
            result.add(entry);
          }
        } else {
          assert normalNodes.get(s.getLastInstructionIndex()) != null;
          result.add(normalNodes.get(s.getLastInstructionIndex()));
        }
      }
      return result.iterator();
    } else {
      assert normalNodes.get(eb.instructionIndex - 1) != null;
      return NonNullSingletonIterator.make(normalNodes.get(eb.instructionIndex - 1));
    }
  }

  public int getSuccNodeCount(IExplodedBasicBlock N) throws UnimplementedError {
    return Iterator2Collection.toSet(getSuccNodes(N)).size();
  }

  /*
   * @see com.ibm.wala.util.graph.EdgeManager#getSuccNodes(java.lang.Object)
   */
  public Iterator getSuccNodes(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.isExitBlock()) {
      return EmptyIterator.instance();
    }
    if (eb.isEntryBlock()) {
      IExplodedBasicBlock z = normalNodes.get(0);
      return z == null ? EmptyIterator. instance() : NonNullSingletonIterator.make(z);
    }
    if (eb.instructionIndex == eb.original.getLastInstructionIndex()) {
      List result = new ArrayList();
      for (Iterator it = ir.getControlFlowGraph().getSuccNodes(eb.original); it.hasNext();) {
        ISSABasicBlock s = it.next();
        if (s.equals(ir.getControlFlowGraph().exit())) {
          result.add(exit());
        } else {
          // there can be a weird corner case where a void method without a
          // return statement
          // can have trailing empty basic blocks with no instructions. ignore
          // these.
          if (normalNodes.get(s.getFirstInstructionIndex()) != null) {
            result.add(normalNodes.get(s.getFirstInstructionIndex()));
          }
        }
      }
      return result.iterator();
    } else {
      assert normalNodes.get(eb.instructionIndex + 1) != null;
      return NonNullSingletonIterator.make(normalNodes.get(eb.instructionIndex + 1));
    }
  }

  public boolean hasEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnimplementedError {
    Assertions.UNREACHABLE();
    return false;
  }

  public void removeAllIncidentEdges(IExplodedBasicBlock node) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();

  }

  public void removeEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public void removeIncomingEdges(IExplodedBasicBlock node) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public void removeOutgoingEdges(IExplodedBasicBlock node) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public int getMaxNumber() {
    return getNumberOfNodes() - 1;
  }

  public IExplodedBasicBlock getNode(int number) {
    if (number == 0) {
      return entry();
    } else if (number == getNumberOfNodes() - 1) {
      return exit();
    } else {
      return normalNodes.get(number - 1);
    }
  }

  public int getNumber(IExplodedBasicBlock n) throws IllegalArgumentException {
    if (n == null) {
      throw new IllegalArgumentException("n == null");
    }
    return n.getNumber();
  }

  public Iterator iterateNodes(IntSet s) throws UnimplementedError {
    Assertions.UNREACHABLE();

  public IntSet getPredNodeNumbers(IExplodedBasicBlock node) {
    MutableSparseIntSet result = MutableSparseIntSet.makeEmpty();
    for (Iterator it = getPredNodes(node); it.hasNext();) {
      result.add(getNumber(it.next()));
    }
    return result;
  }

  public IntSet getSuccNodeNumbers(IExplodedBasicBlock node) throws UnimplementedError {
    Assertions.UNREACHABLE();
    return null;
  }

  /**
   * A basic block with exactly one normal instruction (which may be null), corresponding to a single instruction index in the SSA
   * instruction array.
   * 
   * The block may also have phis.
   */
  private class ExplodedBasicBlock implements IExplodedBasicBlock {

    private final int instructionIndex;

    private final ISSABasicBlock original;

    public ExplodedBasicBlock(int instructionIndex, ISSABasicBlock original) {
      this.instructionIndex = instructionIndex;
      this.original = original;
    }

    @SuppressWarnings("unused")
    public ExplodedControlFlowGraph getExplodedCFG() {
      return ExplodedControlFlowGraph.this;
    }

    public Iterator getCaughtExceptionTypes() {
      if (original instanceof ExceptionHandlerBasicBlock) {
        ExceptionHandlerBasicBlock eb = (ExceptionHandlerBasicBlock) original;
        return eb.getCaughtExceptionTypes();
        return EmptyIterator.instance();
      }
    }

    public int getFirstInstructionIndex() {
      return instructionIndex;
    }

    public int getLastInstructionIndex() {
      return instructionIndex;
    }

    public IMethod getMethod() {
      return ExplodedControlFlowGraph.this.getMethod();
    }

    public int getNumber() {
      if (isEntryBlock()) {
        return 0;
      } else if (isExitBlock()) {
        return getNumberOfNodes() - 1;
      } else {
        return instructionIndex + 1;
      }
    }

    public boolean isCatchBlock() {
      if (original == null) {
        return false;
      }
      return (original.isCatchBlock() && instructionIndex == original.getFirstInstructionIndex());
    }

    public SSAGetCaughtExceptionInstruction getCatchInstruction() {
      if (!(original instanceof ExceptionHandlerBasicBlock)) {
        throw new IllegalArgumentException("block does not represent an exception handler");
      }
      ExceptionHandlerBasicBlock e = (ExceptionHandlerBasicBlock) original;
      return e.getCatchInstruction();
    }

    public boolean isEntryBlock() {
      return instructionIndex == ENTRY_INDEX;
    }

    public boolean isExitBlock() {
      return instructionIndex == EXIT_INDEX;
    }

    public int getGraphNodeId() {
      return getNumber();
    }

    public void setGraphNodeId(int number) {
      Assertions.UNREACHABLE();

    }

    public Iterator iterator() {
      if (isEntryBlock() || isExitBlock() || getInstruction() == null) {
        return EmptyIterator.instance();
      } else {
        return NonNullSingletonIterator.make(getInstruction());
      }
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + instructionIndex;
      result = prime * result + ((original == null) ? 0 : original.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      final ExplodedBasicBlock other = (ExplodedBasicBlock) obj;
      if (instructionIndex != other.instructionIndex)
        return false;
      if (original == null) {
        if (other.original != null)
          return false;
      } else if (!original.equals(other.original))
        return false;
      return true;
    }

    public SSAInstruction getInstruction() {
      if (isEntryBlock() || isExitBlock()) {
        return null;
      } else {
        return ir.getInstructions()[instructionIndex];
      }
    }

    public SSAInstruction getLastInstruction() {
      if (getLastInstructionIndex() < 0) {
        return null;
      } else {
        return ir.getInstructions()[getLastInstructionIndex()];
      }
    }

    public Iterator iteratePhis() {
      if (isEntryBlock() || isExitBlock() || instructionIndex != original.getFirstInstructionIndex()) {
        return EmptyIterator.instance();
      } else {
        return original.iteratePhis();
      }
    }

    public Iterator iteratePis() {
      if (isEntryBlock() || isExitBlock() || instructionIndex != original.getLastInstructionIndex()) {
        return EmptyIterator.instance();
      } else {
        return original.iteratePis();
      }
    }

    @Override
    public String toString() {
      if (isEntryBlock()) {
        return "ExplodedBlock[" + getNumber() + "](entry:" + getMethod() + ")";
      }
      if (isExitBlock()) {
        return "ExplodedBlock[" + getNumber() + "](exit:" + getMethod() + ")";
      }
      return "ExplodedBlock[" + getNumber() + "](original:" + original + ")";
    }
  }

  @Override
  public String toString() {
    StringBuffer s = new StringBuffer("");
    for (Iterator it = iterator(); it.hasNext();) {
      IExplodedBasicBlock bb = it.next();
      s.append("BB").append(getNumber(bb)).append("\n");

      Iterator succNodes = getSuccNodes(bb);
      while (succNodes.hasNext()) {
        s.append("    -> BB").append(getNumber(succNodes.next())).append("\n");
      }
    }
    return s.toString();
  }

  public IR getIR() {
    return ir;
  }

>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
}
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.ssa.analysis;

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

import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSACFG.ExceptionHandlerBasicBlock;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.collections.SimpleVector;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
  }
import com.ibm.wala.util.intset.BitVector;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.MutableSparseIntSet;

/**
 * A view of a control flow graph where each basic block corresponds to exactly one SSA instruction index.
 * 
 * Prototype: Not terribly efficient.
 */
public class ExplodedControlFlowGraph implements ControlFlowGraph {

  private final static int ENTRY_INDEX = -1;

  private final static int EXIT_INDEX = -2;

  private final IR ir;

  /**
   * The ith element of this vector is the basic block holding instruction i. this basic block has number i+1.
   */
  private final SimpleVector normalNodes = new SimpleVector();

  private final Collection allNodes = HashSetFactory.make();

  private final IExplodedBasicBlock entry;

  private final IExplodedBasicBlock exit;

  private ExplodedControlFlowGraph(IR ir) {
    assert ir != null;
    this.ir = ir;
    this.entry = new ExplodedBasicBlock(ENTRY_INDEX, null);
    this.exit = new ExplodedBasicBlock(EXIT_INDEX, null);
    createNodes();
  }

  private void createNodes() {
    allNodes.add(entry);
    allNodes.add(exit);
    for (ISSABasicBlock b : ir.getControlFlowGraph()) {
      for (int i = b.getFirstInstructionIndex(); i <= b.getLastInstructionIndex(); i++) {
        IExplodedBasicBlock bb = new ExplodedBasicBlock(i, b);
        normalNodes.set(i, bb);
        allNodes.add(bb);
      }
    }
  }

  public static ExplodedControlFlowGraph make(IR ir) {
    if (ir == null) {
      throw new IllegalArgumentException("ir == null");
    }
    return new ExplodedControlFlowGraph(ir);
  }

  public IExplodedBasicBlock entry() {
    return entry;
  }

  public IExplodedBasicBlock exit() {
    return exit;
  }

  public IExplodedBasicBlock getBlockForInstruction(int index) {
    return normalNodes.get(index);
  }

  /*
   * @see com.ibm.wala.cfg.ControlFlowGraph#getCatchBlocks()
   */
  public BitVector getCatchBlocks() {
    BitVector original = ir.getControlFlowGraph().getCatchBlocks();
    BitVector result = new BitVector();
    for (int i = 0; i <= original.max(); i++) {
      if (original.get(i)) {
        result.set(i + 1);
      }
    }
    return result;
  }

  public Collection getExceptionalPredecessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.equals(entry)) {
      return Collections.emptySet();
    }
    if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      List result = new ArrayList();
      for (ISSABasicBlock s : ir.getControlFlowGraph().getExceptionalPredecessors(eb.original)) {
        assert normalNodes.get(s.getLastInstructionIndex()) != null;
        result.add(normalNodes.get(s.getLastInstructionIndex()));
      }
      return result;
    } else {
      return Collections.emptySet();
    }
  }

  public List getExceptionalSuccessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;    
    assert eb != null;
    if (eb.equals(exit)) {
      return Collections.emptyList();
    }
    if (eb.isEntryBlock() || eb.instructionIndex == eb.original.getLastInstructionIndex()) {
      List result = new ArrayList();
/** BEGIN Custom change: fix */
      ISSABasicBlock orig = eb.original;
      if (eb.isEntryBlock() && orig == null) {
        orig = ir.getControlFlowGraph().entry();
      }
      
      for (ISSABasicBlock s : ir.getControlFlowGraph().getExceptionalSuccessors(orig)) {
/** END Custom change: fix */
        if (s.equals(ir.getControlFlowGraph().exit())) {
          result.add(exit());
        } else {
          assert normalNodes.get(s.getFirstInstructionIndex()) != null;
          result.add(normalNodes.get(s.getFirstInstructionIndex()));
        }
      }
      return result;
    } else {
      return Collections.emptyList();
    }
  }

  public SSAInstruction[] getInstructions() {
    return ir.getInstructions();
  }

  public IMethod getMethod() throws UnimplementedError {
    return ir.getMethod();
  }

  public Collection getNormalPredecessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.equals(entry)) {
      return Collections.emptySet();
    }
    if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      List result = new ArrayList();
      for (ISSABasicBlock s : ir.getControlFlowGraph().getNormalPredecessors(eb.original)) {
        if (s.equals(ir.getControlFlowGraph().entry())) {
          if (s.getLastInstructionIndex() >= 0) {
            assert normalNodes.get(s.getLastInstructionIndex()) != null;
            result.add(normalNodes.get(s.getLastInstructionIndex()));
          } else {
            result.add(entry());
          }
        } else {
          assert normalNodes.get(s.getLastInstructionIndex()) != null;
          result.add(normalNodes.get(s.getLastInstructionIndex()));
        }
      }
      return result;
    } else {
      assert normalNodes.get(eb.instructionIndex - 1) != null;
      return Collections.singleton(normalNodes.get(eb.instructionIndex - 1));
    }
  }

  public Collection getNormalSuccessors(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.equals(exit)) {
      return Collections.emptySet();
    }
    if (eb.isEntryBlock()) {
      return Collections.singleton(normalNodes.get(0));
    }
    if (eb.instructionIndex == eb.original.getLastInstructionIndex()) {
      List result = new ArrayList();
      for (ISSABasicBlock s : ir.getControlFlowGraph().getNormalSuccessors(eb.original)) {
        if (s.equals(ir.getControlFlowGraph().exit())) {
          result.add(exit());
        } else {
          assert normalNodes.get(s.getFirstInstructionIndex()) != null;
          result.add(normalNodes.get(s.getFirstInstructionIndex()));
        }
      }
      return result;
    } else {
      assert normalNodes.get(eb.instructionIndex + 1) != null;
      return Collections.singleton(normalNodes.get(eb.instructionIndex + 1));
    }
  }

  /*
   * @see com.ibm.wala.cfg.ControlFlowGraph#getProgramCounter(int)
   */
  public int getProgramCounter(int index) throws UnimplementedError {
    return ir.getControlFlowGraph().getProgramCounter(index);
  }

  public void removeNodeAndEdges(IExplodedBasicBlock N) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();

  public void addNode(IExplodedBasicBlock n) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public boolean containsNode(IExplodedBasicBlock N) {
    return allNodes.contains(N);
  }

  public int getNumberOfNodes() {
    return allNodes.size();
  }

  public Iterator iterator() {
    return allNodes.iterator();
  }

  public void removeNode(IExplodedBasicBlock n) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public void addEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public int getPredNodeCount(IExplodedBasicBlock bb) throws IllegalArgumentException {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;    
    if (eb == null) {
      throw new IllegalArgumentException("eb == null");
    }
    if (eb.isEntryBlock()) {
      return 0;
    }
    if (eb.isExitBlock()) {
      return ir.getControlFlowGraph().getPredNodeCount(ir.getControlFlowGraph().exit());
    }
    if (eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      if (eb.original.isEntryBlock()) {
        return 1;
      } else {
        return ir.getControlFlowGraph().getPredNodeCount(eb.original);
      }
    } else {
      return 1;
    }
  }

  public Iterator getPredNodes(IExplodedBasicBlock bb) throws IllegalArgumentException {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    if (eb == null) {
      throw new IllegalArgumentException("eb == null");
    }
    if (eb.isEntryBlock()) {
      return EmptyIterator.instance();
    }
    ISSABasicBlock original = eb.isExitBlock() ? ir.getControlFlowGraph().exit() : eb.original;
    if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) {
      List result = new ArrayList();
      if (eb.original != null && eb.original.isEntryBlock()) {
        result.add(entry);
      }
      for (Iterator it = ir.getControlFlowGraph().getPredNodes(original); it.hasNext();) {
        ISSABasicBlock s = it.next();
        if (s.isEntryBlock()) {
          // it's possible for an entry block to have instructions; in this case, add
          // the exploded basic block for the last instruction in the entry block
          if (s.getLastInstructionIndex() >= 0) {
            result.add(normalNodes.get(s.getLastInstructionIndex()));
          } else {
            result.add(entry);
          }
        } else {
          assert normalNodes.get(s.getLastInstructionIndex()) != null;
          result.add(normalNodes.get(s.getLastInstructionIndex()));
        }
      }
      return result.iterator();
    } else {
      assert normalNodes.get(eb.instructionIndex - 1) != null;
      return NonNullSingletonIterator.make(normalNodes.get(eb.instructionIndex - 1));
    }
  }

  public int getSuccNodeCount(IExplodedBasicBlock N) throws UnimplementedError {
    return Iterator2Collection.toSet(getSuccNodes(N)).size();
  }

  /*
   * @see com.ibm.wala.util.graph.EdgeManager#getSuccNodes(java.lang.Object)
   */
  public Iterator getSuccNodes(IExplodedBasicBlock bb) {
    ExplodedBasicBlock eb = (ExplodedBasicBlock) bb;
    assert eb != null;
    if (eb.isExitBlock()) {
      return EmptyIterator.instance();
    }
    if (eb.isEntryBlock()) {
      IExplodedBasicBlock z = normalNodes.get(0);
      return z == null ? EmptyIterator. instance() : NonNullSingletonIterator.make(z);
    }
    if (eb.instructionIndex == eb.original.getLastInstructionIndex()) {
      List result = new ArrayList();
      for (Iterator it = ir.getControlFlowGraph().getSuccNodes(eb.original); it.hasNext();) {
        ISSABasicBlock s = it.next();
        if (s.equals(ir.getControlFlowGraph().exit())) {
          result.add(exit());
        } else {
          // there can be a weird corner case where a void method without a
          // return statement
          // can have trailing empty basic blocks with no instructions. ignore
          // these.
          if (normalNodes.get(s.getFirstInstructionIndex()) != null) {
            result.add(normalNodes.get(s.getFirstInstructionIndex()));
          }
        }
      }
      return result.iterator();
    } else {
      assert normalNodes.get(eb.instructionIndex + 1) != null;
      return NonNullSingletonIterator.make(normalNodes.get(eb.instructionIndex + 1));
    }
  }

  public boolean hasEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnimplementedError {
/** BEGIN Custom change: implement hasEdge */
    for (Iterator it = getSuccNodes(src); it.hasNext();) {
      IExplodedBasicBlock succ = it.next();
      if (succ == dst) {
        return true;
      }
    }
    
    return false;
/** END Custom change: implement hasEdge */
  }

  public void removeAllIncidentEdges(IExplodedBasicBlock node) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();

  }

  public void removeEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public void removeIncomingEdges(IExplodedBasicBlock node) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public void removeOutgoingEdges(IExplodedBasicBlock node) throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
  }

  public int getMaxNumber() {
    return getNumberOfNodes() - 1;
  }

  public IExplodedBasicBlock getNode(int number) {
    if (number == 0) {
      return entry();
    } else if (number == getNumberOfNodes() - 1) {
      return exit();
    } else {
      return normalNodes.get(number - 1);
    }
  }

  public int getNumber(IExplodedBasicBlock n) throws IllegalArgumentException {
    if (n == null) {
      throw new IllegalArgumentException("n == null");
    }
    return n.getNumber();
  }

  public Iterator iterateNodes(IntSet s) throws UnimplementedError {
    Assertions.UNREACHABLE();
    return null;
  }

  public IntSet getPredNodeNumbers(IExplodedBasicBlock node) {
    MutableSparseIntSet result = MutableSparseIntSet.makeEmpty();
    for (Iterator it = getPredNodes(node); it.hasNext();) {
      result.add(getNumber(it.next()));
    }
    return result;
  }

  public IntSet getSuccNodeNumbers(IExplodedBasicBlock node) throws UnimplementedError {
    Assertions.UNREACHABLE();
    return null;
  }

  /**
   * A basic block with exactly one normal instruction (which may be null), corresponding to a single instruction index in the SSA
   * instruction array.
   * 
   * The block may also have phis.
   */
  private class ExplodedBasicBlock implements IExplodedBasicBlock {

    private final int instructionIndex;

    private final ISSABasicBlock original;

    public ExplodedBasicBlock(int instructionIndex, ISSABasicBlock original) {
      this.instructionIndex = instructionIndex;
      this.original = original;
    }

    @SuppressWarnings("unused")
    public ExplodedControlFlowGraph getExplodedCFG() {
      return ExplodedControlFlowGraph.this;
    }

    public Iterator getCaughtExceptionTypes() {
      if (original instanceof ExceptionHandlerBasicBlock) {
        ExceptionHandlerBasicBlock eb = (ExceptionHandlerBasicBlock) original;
        return eb.getCaughtExceptionTypes();
      } else {
        return EmptyIterator.instance();
      }
    }

    public int getFirstInstructionIndex() {
      return instructionIndex;
    }

    public int getLastInstructionIndex() {
      return instructionIndex;
    }

    public IMethod getMethod() {
      return ExplodedControlFlowGraph.this.getMethod();
    }

    public int getNumber() {
      if (isEntryBlock()) {
        return 0;
      } else if (isExitBlock()) {
        return getNumberOfNodes() - 1;
      } else {
        return instructionIndex + 1;
      }
    }

    public boolean isCatchBlock() {
      if (original == null) {
        return false;
      }
      return (original.isCatchBlock() && instructionIndex == original.getFirstInstructionIndex());
    }

    public SSAGetCaughtExceptionInstruction getCatchInstruction() {
      if (!(original instanceof ExceptionHandlerBasicBlock)) {
        throw new IllegalArgumentException("block does not represent an exception handler");
      }
      ExceptionHandlerBasicBlock e = (ExceptionHandlerBasicBlock) original;
      return e.getCatchInstruction();
    }

    public boolean isEntryBlock() {
      return instructionIndex == ENTRY_INDEX;
    }

    public boolean isExitBlock() {
      return instructionIndex == EXIT_INDEX;
    }

    public int getGraphNodeId() {
      return getNumber();
    }

    public void setGraphNodeId(int number) {
      Assertions.UNREACHABLE();

    }

    public Iterator iterator() {
      if (isEntryBlock() || isExitBlock() || getInstruction() == null) {
        return EmptyIterator.instance();
      } else {
        return NonNullSingletonIterator.make(getInstruction());
      }
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + instructionIndex;
      result = prime * result + ((original == null) ? 0 : original.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      final ExplodedBasicBlock other = (ExplodedBasicBlock) obj;
      if (instructionIndex != other.instructionIndex)
        return false;
      if (original == null) {
        if (other.original != null)
          return false;
      } else if (!original.equals(other.original))
        return false;
      return true;
    }

    public SSAInstruction getInstruction() {
      if (isEntryBlock() || isExitBlock()) {
        return null;
      } else {
        return ir.getInstructions()[instructionIndex];
      }
    }

    public SSAInstruction getLastInstruction() {
      if (getLastInstructionIndex() < 0) {
        return null;
      } else {
        return ir.getInstructions()[getLastInstructionIndex()];
      }
    }

    public Iterator iteratePhis() {
      if (isEntryBlock() || isExitBlock() || instructionIndex != original.getFirstInstructionIndex()) {
        return EmptyIterator.instance();
      } else {
        return original.iteratePhis();
      }
    }

    public Iterator iteratePis() {
      if (isEntryBlock() || isExitBlock() || instructionIndex != original.getLastInstructionIndex()) {
        return EmptyIterator.instance();
      } else {
        return original.iteratePis();
      }
    }

    @Override
    public String toString() {
      if (isEntryBlock()) {
        return "ExplodedBlock[" + getNumber() + "](entry:" + getMethod() + ")";
      }
      if (isExitBlock()) {
        return "ExplodedBlock[" + getNumber() + "](exit:" + getMethod() + ")";
      }
      return "ExplodedBlock[" + getNumber() + "](original:" + original + ")";
    }
  }

  @Override
  public String toString() {
    StringBuffer s = new StringBuffer("");
    for (Iterator it = iterator(); it.hasNext();) {
      IExplodedBasicBlock bb = it.next();
      s.append("BB").append(getNumber(bb)).append("\n");

      Iterator succNodes = getSuccNodes(bb);
      while (succNodes.hasNext()) {
        s.append("    -> BB").append(getNumber(succNodes.next())).append("\n");
      }
    }
    return s.toString();
  }

  public IR getIR() {
    return ir;
  }

}
File
ExplodedControlFlowGraph.java
Developer's decision
Version 1
Kind of conflict
Annotation
Attribute
Class declaration
Comment
Import
Method declaration
Method invocation
Package declaration
Chunk
Conflicting content
import java.util.Map;
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.types;

import java.io.Serializable;
import java.util.Map;

import com.ibm.wala.util.collections.HashMapFactory;

/**
 * A class to represent the reference in a class file to some type (class, primitive or array). A type reference is
 * uniquely defined by
 * 
    *
  • an initiating class loader *
  • a type name *
* Resolving a TypeReference to a Type can be an expensive operation. Therefore we canonicalize TypeReference instances * and cache the result of resolution. */ public final class TypeReference implements Serializable { /* Serial version */ private static final long serialVersionUID = -3256390509887654327L; /** * NOTE: initialisation order is important! * * TypeReferences are canonical. */ /** * Used for fast access to primitives. Primitives appear in the main dictionary also. */ private final static Map primitiveMap = HashMapFactory.make(); /** * Used to canonicalize TypeReferences. */ private final static Map dictionary = HashMapFactory.make(); /********************************************************************************************************************* * Primitive Dispatch * ********************************************************************************************************************/ public final static TypeName BooleanName = TypeName.string2TypeName("Z"); public final static byte BooleanTypeCode = 'Z'; public final static TypeReference Boolean = makePrimitive(BooleanName); public final static TypeName ByteName = TypeName.string2TypeName("B"); public final static byte ByteTypeCode = 'B'; public final static TypeReference Byte = makePrimitive(ByteName); public final static TypeName CharName = TypeName.string2TypeName("C"); public final static byte CharTypeCode = 'C'; public final static TypeReference Char = makePrimitive(CharName); public final static TypeName DoubleName = TypeName.string2TypeName("D"); public final static byte DoubleTypeCode = 'D'; public final static TypeReference Double = makePrimitive(DoubleName); public final static TypeName FloatName = TypeName.string2TypeName("F"); public final static byte FloatTypeCode = 'F'; public final static TypeReference Float = makePrimitive(FloatName); public final static TypeName IntName = TypeName.string2TypeName("I"); public final static byte IntTypeCode = 'I'; public final static TypeReference Int = makePrimitive(IntName); public final static TypeName LongName = TypeName.string2TypeName("J"); public final static byte LongTypeCode = 'J'; public final static TypeReference Long = makePrimitive(LongName); public final static TypeName ShortName = TypeName.string2TypeName("S"); public final static byte ShortTypeCode = 'S'; public final static TypeReference Short = makePrimitive(ShortName); public final static TypeName VoidName = TypeName.string2TypeName("V"); public final static byte VoidTypeCode = 'V'; public final static TypeReference Void = makePrimitive(VoidName); public final static byte OtherPrimitiveTypeCode = 'P'; /********************************************************************************************************************* * Primitive Array Dispatch * ********************************************************************************************************************/ public final static TypeReference BooleanArray = findOrCreateArrayOf(Boolean); public final static TypeReference ByteArray = findOrCreateArrayOf(Byte); public final static TypeReference CharArray = findOrCreateArrayOf(Char); public final static TypeReference DoubleArray = findOrCreateArrayOf(Double); public final static TypeReference FloatArray = findOrCreateArrayOf(Float); public final static TypeReference IntArray = findOrCreateArrayOf(Int); public final static TypeReference LongArray = findOrCreateArrayOf(Long); public final static TypeReference ShortArray = findOrCreateArrayOf(Short); /********************************************************************************************************************* * Special object types * ********************************************************************************************************************/ private final static TypeName JavaLangArithmeticExceptionName = TypeName.string2TypeName("Ljava/lang/ArithmeticException"); public final static TypeReference JavaLangArithmeticException = findOrCreate(ClassLoaderReference.Primordial, JavaLangArithmeticExceptionName); private final static TypeName JavaLangArrayStoreExceptionName = TypeName.string2TypeName("Ljava/lang/ArrayStoreException"); public final static TypeReference JavaLangArrayStoreException = findOrCreate(ClassLoaderReference.Primordial, JavaLangArrayStoreExceptionName); private final static TypeName JavaLangArrayIndexOutOfBoundsExceptionName = TypeName .string2TypeName("Ljava/lang/ArrayIndexOutOfBoundsException"); public final static TypeReference JavaLangArrayIndexOutOfBoundsException = findOrCreate(ClassLoaderReference.Primordial, JavaLangArrayIndexOutOfBoundsExceptionName); private final static TypeName JavaLangClassName = TypeName.string2TypeName("Ljava/lang/Class"); public final static TypeReference JavaLangClass = findOrCreate(ClassLoaderReference.Primordial, JavaLangClassName); private final static TypeName JavaLangClassCastExceptionName = TypeName.string2TypeName("Ljava/lang/ClassCastException"); public final static TypeReference JavaLangClassCastException = findOrCreate(ClassLoaderReference.Primordial, JavaLangClassCastExceptionName); private final static TypeName JavaLangComparableName = TypeName.string2TypeName("Ljava/lang/Comparable"); public final static TypeReference JavaLangComparable = findOrCreate(ClassLoaderReference.Primordial, JavaLangComparableName); private final static TypeName JavaLangReflectConstructorName = TypeName.string2TypeName("Ljava/lang/reflect/Constructor"); public final static TypeReference JavaLangReflectConstructor = findOrCreate(ClassLoaderReference.Primordial, JavaLangReflectConstructorName); private final static TypeName JavaLangReflectMethodName = TypeName.string2TypeName("Ljava/lang/reflect/Method"); public final static TypeReference JavaLangReflectMethod = findOrCreate(ClassLoaderReference.Primordial, JavaLangReflectMethodName); private final static TypeName JavaLangEnumName = TypeName.string2TypeName("Ljava/lang/Enum"); public final static TypeReference JavaLangEnum = findOrCreate(ClassLoaderReference.Primordial, JavaLangEnumName); private final static TypeName JavaLangErrorName = TypeName.string2TypeName("Ljava/lang/Error"); public final static TypeReference JavaLangError = findOrCreate(ClassLoaderReference.Primordial, JavaLangErrorName); private final static TypeName JavaLangExceptionName = TypeName.string2TypeName("Ljava/lang/Exception"); public final static TypeReference JavaLangException = findOrCreate(ClassLoaderReference.Primordial, JavaLangExceptionName); private final static TypeName JavaLangNegativeArraySizeExceptionName = TypeName .string2TypeName("Ljava/lang/NegativeArraySizeException"); public final static TypeReference JavaLangNegativeArraySizeException = findOrCreate(ClassLoaderReference.Primordial, JavaLangNegativeArraySizeExceptionName); private final static TypeName JavaLangNullPointerExceptionName = TypeName.string2TypeName("Ljava/lang/NullPointerException"); public final static TypeReference JavaLangNullPointerException = findOrCreate(ClassLoaderReference.Primordial, JavaLangNullPointerExceptionName); private final static TypeName JavaLangRuntimeExceptionName = TypeName.string2TypeName("Ljava/lang/RuntimeException"); public final static TypeReference JavaLangRuntimeException = findOrCreate(ClassLoaderReference.Primordial, JavaLangRuntimeExceptionName); private final static TypeName JavaLangClassNotFoundExceptionName = TypeName.string2TypeName("Ljava/lang/ClassNotFoundException"); public final static TypeReference JavaLangClassNotFoundException = findOrCreate(ClassLoaderReference.Primordial, JavaLangClassNotFoundExceptionName); private final static TypeName JavaLangOutOfMemoryErrorName = TypeName.string2TypeName("Ljava/lang/OutOfMemoryError"); public final static TypeReference JavaLangOutOfMemoryError = findOrCreate(ClassLoaderReference.Primordial, JavaLangOutOfMemoryErrorName); private final static TypeName JavaLangExceptionInInitializerErrorName = TypeName .string2TypeName("Ljava/lang/ExceptionInInitializerError"); public final static TypeReference JavaLangExceptionInInitializerError = findOrCreate(ClassLoaderReference.Primordial, JavaLangExceptionInInitializerErrorName); private final static TypeName JavaLangObjectName = TypeName.string2TypeName("Ljava/lang/Object"); public final static TypeReference JavaLangObject = findOrCreate(ClassLoaderReference.Primordial, JavaLangObjectName); private final static TypeName JavaLangStackTraceElementName = TypeName.string2TypeName("Ljava/lang/StackTraceElement"); * The type name public final static TypeReference JavaLangStackTraceElement = findOrCreate(ClassLoaderReference.Primordial, JavaLangStackTraceElementName); private final static TypeName JavaLangStringName = TypeName.string2TypeName("Ljava/lang/String"); public final static TypeReference JavaLangString = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringName); private final static TypeName JavaLangStringBufferName = TypeName.string2TypeName("Ljava/lang/StringBuffer"); public final static TypeReference JavaLangStringBuffer = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringBufferName); private final static TypeName JavaLangStringBuilderName = TypeName.string2TypeName("Ljava/lang/StringBuilder"); public final static TypeReference JavaLangStringBuilder = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringBuilderName); private final static TypeName JavaLangThreadName = TypeName.string2TypeName("Ljava/lang/Thread"); public final static TypeReference JavaLangThread = findOrCreate(ClassLoaderReference.Primordial, JavaLangThreadName); private final static TypeName JavaLangThrowableName = TypeName.string2TypeName("Ljava/lang/Throwable"); public final static TypeReference JavaLangThrowable = findOrCreate(ClassLoaderReference.Primordial, JavaLangThrowableName); public final static TypeName JavaLangCloneableName = TypeName.string2TypeName("Ljava/lang/Cloneable"); public final static TypeReference JavaLangCloneable = findOrCreate(ClassLoaderReference.Primordial, JavaLangCloneableName); private final static TypeName JavaLangSystemName = TypeName.string2TypeName("Ljava/lang/System"); public final static TypeReference JavaLangSystem = findOrCreate(ClassLoaderReference.Primordial, JavaLangSystemName); private final static TypeName JavaLangIntegerName = TypeName.string2TypeName("Ljava/lang/Integer"); public final static TypeReference JavaLangInteger = findOrCreate(ClassLoaderReference.Primordial, JavaLangIntegerName); private final static TypeName JavaLangBooleanName = TypeName.string2TypeName("Ljava/lang/Boolean"); public final static TypeReference JavaLangBoolean = findOrCreate(ClassLoaderReference.Primordial, JavaLangBooleanName); private final static TypeName JavaLangDoubleName = TypeName.string2TypeName("Ljava/lang/Double"); public final static TypeReference JavaLangDouble = findOrCreate(ClassLoaderReference.Primordial, JavaLangDoubleName); private final static TypeName JavaLangFloatName = TypeName.string2TypeName("Ljava/lang/Float"); public final static TypeReference JavaLangFloat = findOrCreate(ClassLoaderReference.Primordial, JavaLangFloatName); private final static TypeName JavaLangShortName = TypeName.string2TypeName("Ljava/lang/Short"); public final static TypeReference JavaLangShort = findOrCreate(ClassLoaderReference.Primordial, JavaLangShortName); private final static TypeName JavaLangLongName = TypeName.string2TypeName("Ljava/lang/Long"); public final static TypeReference JavaLangLong = findOrCreate(ClassLoaderReference.Primordial, JavaLangLongName); private final static TypeName JavaLangByteName = TypeName.string2TypeName("Ljava/lang/Byte"); public final static TypeReference JavaLangByte = findOrCreate(ClassLoaderReference.Primordial, JavaLangByteName); private final static TypeName JavaLangCharacterName = TypeName.string2TypeName("Ljava/lang/Character"); public final static TypeReference JavaLangCharacter = findOrCreate(ClassLoaderReference.Primordial, JavaLangCharacterName); public final static TypeName JavaIoSerializableName = TypeName.string2TypeName("Ljava/io/Serializable"); public final static TypeReference JavaIoSerializable = findOrCreate(ClassLoaderReference.Primordial, JavaIoSerializableName); private final static TypeName JavaUtilCollectionName = TypeName.string2TypeName("Ljava/util/Collection"); public final static TypeReference JavaUtilCollection = findOrCreate(ClassLoaderReference.Primordial, JavaUtilCollectionName); private final static TypeName JavaUtilMapName = TypeName.string2TypeName("Ljava/util/Map"); public final static TypeReference JavaUtilMap = findOrCreate(ClassLoaderReference.Primordial, JavaUtilMapName); private final static TypeName JavaUtilHashSetName = TypeName.string2TypeName("Ljava/util/HashSet"); public final static TypeReference JavaUtilHashSet = findOrCreate(ClassLoaderReference.Primordial, JavaUtilHashSetName); private final static TypeName JavaUtilSetName = TypeName.string2TypeName("Ljava/util/Set"); public final static TypeReference JavaUtilSet = findOrCreate(ClassLoaderReference.Primordial, JavaUtilSetName); private final static TypeName JavaUtilEnumName = TypeName.string2TypeName("Ljava/util/Enumeration"); public final static TypeReference JavaUtilEnum = findOrCreate(ClassLoaderReference.Primordial, JavaUtilEnumName); private final static TypeName JavaUtilIteratorName = TypeName.string2TypeName("Ljava/util/Iterator"); public final static TypeReference JavaUtilIterator = findOrCreate(ClassLoaderReference.Primordial, JavaUtilIteratorName); private final static TypeName JavaUtilVectorName = TypeName.string2TypeName("Ljava/util/Vector"); public final static TypeReference JavaUtilVector = findOrCreate(ClassLoaderReference.Primordial, JavaUtilVectorName); public final static byte ClassTypeCode = 'L'; public final static byte ArrayTypeCode = '['; // TODO! the following two are unsound hacks; kill them. final static TypeName NullName = TypeName.string2TypeName("null"); public final static TypeReference Null = findOrCreate(ClassLoaderReference.Primordial, NullName); // TODO: is the following necessary. Used only by ShrikeBT. final static TypeName UnknownName = TypeName.string2TypeName("?unknown?"); public final static TypeReference Unknown = findOrCreate(ClassLoaderReference.Primordial, UnknownName); private static TypeReference makePrimitive(TypeName n) { return makePrimitive(ClassLoaderReference.Primordial, n); } public static TypeReference makePrimitive(ClassLoaderReference cl, TypeName n) { TypeReference t = new TypeReference(cl, n); primitiveMap.put(t.name, t); return t; } /** * Could name a represent a primitive type? */ public static boolean isPrimitiveType(TypeName name) { return name.isPrimitiveType(); } /** * The initiating class loader */ private final ClassLoaderReference classloader; /** * The type name */ private final TypeName name; /** * Find or create the canonical TypeReference instance for the given pair. * * @param cl the classloader (defining/initiating depending on usage) */ public static synchronized TypeReference findOrCreate(ClassLoaderReference cl, TypeName typeName) { if (cl == null) { throw new IllegalArgumentException("null cl"); } TypeReference p = primitiveMap.get(typeName); if (p != null) { return p; } // Next actually findOrCreate the type reference using the proper // classloader. // [This is the only allocation site for TypeReference] if (typeName.isArrayType()) { TypeName e = typeName.getInnermostElementType(); if (e.isPrimitiveType()) { cl = ClassLoaderReference.Primordial; } } Key key = new Key(cl, typeName); TypeReference val = dictionary.get(key); if (val != null) { return val; } else { val = new TypeReference(cl, typeName); dictionary.put(key, val); return val; } } /** * Find or create the canonical {@link TypeReference} instance for the given pair. * * @param cl the classloader (defining/initiating depending on usage) */ * @param typeName something like "Ljava/util/Arrays" */ public static synchronized TypeReference findOrCreate(ClassLoaderReference cl, String typeName) { return findOrCreate(cl, TypeName.string2TypeName(typeName)); } /** BEGIN Custom change: search types */ public static synchronized TypeReference find(ClassLoaderReference cl, String typeName) { return find(cl, TypeName.string2TypeName(typeName)); } /** * Find the canonical TypeReference instance for the given pair. May return null. * * @param cl the classloader (defining/initiating depending on usage) */ public static synchronized TypeReference find(ClassLoaderReference cl, TypeName typeName) { if (cl == null) { throw new IllegalArgumentException("null cl"); } TypeReference p = primitiveMap.get(typeName); if (p != null) { return p; } // Next actually findOrCreate the type reference using the proper // classloader. // [This is the only allocation site for TypeReference] if (typeName.isArrayType()) { TypeName e = typeName.getInnermostElementType(); if (e.isPrimitiveType()) { cl = ClassLoaderReference.Primordial; } } Key key = new Key(cl, typeName); TypeReference val = dictionary.get(key); return val; } /** END Custom change: search types */ public static TypeReference findOrCreateArrayOf(TypeReference t) { if (t == null) { throw new IllegalArgumentException("t is null"); } TypeName name = t.getName(); if (t.isPrimitiveType()) { return findOrCreate(ClassLoaderReference.Primordial, name.getArrayTypeForElementType()); } else { return findOrCreate(t.getClassLoader(), name.getArrayTypeForElementType()); } } /** * NB: All type names should use '/' and not '.' as a separator. eg. Ljava/lang/Class * * @param cl the classloader * @param tn the type name */ } protected TypeReference(ClassLoaderReference cl, TypeName tn) { classloader = cl; name = tn; } /** * @return the classloader component of this type reference */ public final ClassLoaderReference getClassLoader() { return classloader; } /** * @return the type name component of this type reference */ public final TypeName getName() { return name; } /** * TODO: specialized form of TypeReference for arrays, please. Get the element type of for this array type. */ public final TypeReference getArrayElementType() { TypeName element = name.parseForArrayElementName(); return findOrCreate(classloader, element); } /** * Get array type corresponding to "this" array element type. */ public final TypeReference getArrayTypeForElementType() { return findOrCreate(classloader, name.getArrayTypeForElementType()); } /** * Return the dimensionality of the type. By convention, class types have dimensionality 0, primitives -1, and arrays * the number of [ in their descriptor. */ public final int getDimensionality() { return name.getDimensionality(); } /** * Return the innermost element type reference for an array */ public final TypeReference getInnermostElementType() { return findOrCreate(classloader, name.getInnermostElementType()); } /** * Does 'this' refer to a class? */ public final boolean isClassType() { return !isArrayType() && !isPrimitiveType(); } /** * Does 'this' refer to an array? */ public final boolean isArrayType() { return name.isArrayType(); } /** * Does 'this' refer to a primitive type */ public final boolean isPrimitiveType() { return isPrimitiveType(name); } /** * Does 'this' refer to a reference type */ public final boolean isReferenceType() { return !isPrimitiveType(); } @Override public final int hashCode() { return name.hashCode(); } /** * TypeReferences are canonical. However, note that two TypeReferences can be non-equal, yet still represent the same * IClass. * * For example, the there can be two TypeReferences and . * These two TypeReference are NOT equal(), but they both represent the IClass which is named * */ @Override public final boolean equals(Object other) { return (this == other); } @Override public final String toString() { return "<" + classloader.getName() + "," + name + ">"; } public static TypeReference findOrCreateClass(ClassLoaderReference loader, String packageName, String className) { TypeName tn = TypeName.findOrCreateClassName(packageName, className); return findOrCreate(loader, tn); } private static class Key { /** * The initiating class loader */ private final ClassLoaderReference classloader; /** * The type name */ private final TypeName name; Key(ClassLoaderReference classloader, TypeName name) { this.classloader = classloader; this.name = name; } @Override public final int hashCode() { return name.hashCode(); } @Override public final boolean equals(Object other) { assert other != null && other instanceof Key; Key that = (Key) other; return (name.equals(that.name) && classloader.equals(that.classloader)); } } } ======= /******************************************************************************* * Copyright (c) 2002 - 2006 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.types; import static com.ibm.wala.types.TypeName.ArrayMask; import static com.ibm.wala.types.TypeName.ElementBits; import static com.ibm.wala.types.TypeName.PrimitiveMask; import java.io.Serializable; import com.ibm.wala.util.collections.HashMapFactory; /** * A class to represent the reference in a class file to some type (class, primitive or array). A type reference is * uniquely defined by *
    *
  • an initiating class loader *
  • a type name *
* Resolving a TypeReference to a Type can be an expensive operation. Therefore we canonicalize TypeReference instances * and cache the result of resolution. */ public final class TypeReference implements Serializable { /* Serial version */ private static final long serialVersionUID = -3256390509887654327L; /** * NOTE: initialisation order is important! * * TypeReferences are canonical. */ /** * Used for fast access to primitives. Primitives appear in the main dictionary also. */ private final static Map primitiveMap = HashMapFactory.make(); /** * Used to canonicalize TypeReferences. */ private final static Map dictionary = HashMapFactory.make(); /********************************************************************************************************************* * Primitive Dispatch * ********************************************************************************************************************/ public final static TypeName BooleanName = TypeName.string2TypeName("Z"); public final static byte BooleanTypeCode = 'Z'; public final static TypeReference Boolean = makePrimitive(BooleanName); public final static TypeName ByteName = TypeName.string2TypeName("B"); public final static byte ByteTypeCode = 'B'; public final static TypeReference Byte = makePrimitive(ByteName); public final static TypeName CharName = TypeName.string2TypeName("C"); public final static byte CharTypeCode = 'C'; public final static TypeReference Char = makePrimitive(CharName); public final static TypeName DoubleName = TypeName.string2TypeName("D"); public final static byte DoubleTypeCode = 'D'; public final static TypeReference Double = makePrimitive(DoubleName); public final static TypeName FloatName = TypeName.string2TypeName("F"); public final static byte FloatTypeCode = 'F'; public final static TypeReference Float = makePrimitive(FloatName); public final static TypeName IntName = TypeName.string2TypeName("I"); public final static byte IntTypeCode = 'I'; public final static TypeReference Int = makePrimitive(IntName); public final static TypeName LongName = TypeName.string2TypeName("J"); public final static byte LongTypeCode = 'J'; public final static TypeReference Long = makePrimitive(LongName); public final static TypeName ShortName = TypeName.string2TypeName("S"); public final static byte ShortTypeCode = 'S'; public final static TypeReference Short = makePrimitive(ShortName); public final static TypeName VoidName = TypeName.string2TypeName("V"); public final static byte VoidTypeCode = 'V'; public final static TypeReference Void = makePrimitive(VoidName); public final static byte OtherPrimitiveTypeCode = 'P'; /********************************************************************************************************************* * Primitive Array Dispatch * ********************************************************************************************************************/ @Override public final static TypeReference BooleanArray = findOrCreateArrayOf(Boolean); public final static TypeReference ByteArray = findOrCreateArrayOf(Byte); public final static TypeReference CharArray = findOrCreateArrayOf(Char); public final static TypeReference DoubleArray = findOrCreateArrayOf(Double); public final static TypeReference FloatArray = findOrCreateArrayOf(Float); public final static TypeReference IntArray = findOrCreateArrayOf(Int); public final static TypeReference LongArray = findOrCreateArrayOf(Long); public final static TypeReference ShortArray = findOrCreateArrayOf(Short); /********************************************************************************************************************* * Special object types * ********************************************************************************************************************/ private final static TypeName JavaLangArithmeticExceptionName = TypeName.string2TypeName("Ljava/lang/ArithmeticException"); public final static TypeReference JavaLangArithmeticException = findOrCreate(ClassLoaderReference.Primordial, JavaLangArithmeticExceptionName); private final static TypeName JavaLangArrayStoreExceptionName = TypeName.string2TypeName("Ljava/lang/ArrayStoreException"); public final static TypeReference JavaLangArrayStoreException = findOrCreate(ClassLoaderReference.Primordial, JavaLangArrayStoreExceptionName); private final static TypeName JavaLangArrayIndexOutOfBoundsExceptionName = TypeName .string2TypeName("Ljava/lang/ArrayIndexOutOfBoundsException"); public final static TypeReference JavaLangArrayIndexOutOfBoundsException = findOrCreate(ClassLoaderReference.Primordial, JavaLangArrayIndexOutOfBoundsExceptionName); private final static TypeName JavaLangClassName = TypeName.string2TypeName("Ljava/lang/Class"); public final static TypeReference JavaLangClass = findOrCreate(ClassLoaderReference.Primordial, JavaLangClassName); private final static TypeName JavaLangClassCastExceptionName = TypeName.string2TypeName("Ljava/lang/ClassCastException"); public final static TypeReference JavaLangClassCastException = findOrCreate(ClassLoaderReference.Primordial, JavaLangClassCastExceptionName); private final static TypeName JavaLangComparableName = TypeName.string2TypeName("Ljava/lang/Comparable"); public final static TypeReference JavaLangComparable = findOrCreate(ClassLoaderReference.Primordial, JavaLangComparableName); private final static TypeName JavaLangReflectConstructorName = TypeName.string2TypeName("Ljava/lang/reflect/Constructor"); public final static TypeReference JavaLangReflectConstructor = findOrCreate(ClassLoaderReference.Primordial, JavaLangReflectConstructorName); private final static TypeName JavaLangReflectMethodName = TypeName.string2TypeName("Ljava/lang/reflect/Method"); public final static TypeReference JavaLangReflectMethod = findOrCreate(ClassLoaderReference.Primordial, JavaLangReflectMethodName); private final static TypeName JavaLangEnumName = TypeName.string2TypeName("Ljava/lang/Enum"); public final static TypeReference JavaLangEnum = findOrCreate(ClassLoaderReference.Primordial, JavaLangEnumName); private final static TypeName JavaLangErrorName = TypeName.string2TypeName("Ljava/lang/Error"); public final static TypeReference JavaLangError = findOrCreate(ClassLoaderReference.Primordial, JavaLangErrorName); private final static TypeName JavaLangExceptionName = TypeName.string2TypeName("Ljava/lang/Exception"); public final static TypeReference JavaLangException = findOrCreate(ClassLoaderReference.Primordial, JavaLangExceptionName); private final static TypeName JavaLangNegativeArraySizeExceptionName = TypeName .string2TypeName("Ljava/lang/NegativeArraySizeException"); public final static TypeReference JavaLangNegativeArraySizeException = findOrCreate(ClassLoaderReference.Primordial, JavaLangNegativeArraySizeExceptionName); private final static TypeName JavaLangNullPointerExceptionName = TypeName.string2TypeName("Ljava/lang/NullPointerException"); private final ClassLoaderReference classloader; public final static TypeReference JavaLangNullPointerException = findOrCreate(ClassLoaderReference.Primordial, JavaLangNullPointerExceptionName); private final static TypeName JavaLangRuntimeExceptionName = TypeName.string2TypeName("Ljava/lang/RuntimeException"); public final static TypeReference JavaLangRuntimeException = findOrCreate(ClassLoaderReference.Primordial, JavaLangRuntimeExceptionName); private final static TypeName JavaLangClassNotFoundExceptionName = TypeName.string2TypeName("Ljava/lang/ClassNotFoundException"); public final static TypeReference JavaLangClassNotFoundException = findOrCreate(ClassLoaderReference.Primordial, JavaLangClassNotFoundExceptionName); private final static TypeName JavaLangOutOfMemoryErrorName = TypeName.string2TypeName("Ljava/lang/OutOfMemoryError"); public final static TypeReference JavaLangOutOfMemoryError = findOrCreate(ClassLoaderReference.Primordial, JavaLangOutOfMemoryErrorName); private final static TypeName JavaLangExceptionInInitializerErrorName = TypeName .string2TypeName("Ljava/lang/ExceptionInInitializerError"); public final static TypeReference JavaLangExceptionInInitializerError = findOrCreate(ClassLoaderReference.Primordial, JavaLangExceptionInInitializerErrorName); private final static TypeName JavaLangObjectName = TypeName.string2TypeName("Ljava/lang/Object"); public final static TypeReference JavaLangObject = findOrCreate(ClassLoaderReference.Primordial, JavaLangObjectName); private final static TypeName JavaLangStackTraceElementName = TypeName.string2TypeName("Ljava/lang/StackTraceElement"); public final static TypeReference JavaLangStackTraceElement = findOrCreate(ClassLoaderReference.Primordial, JavaLangStackTraceElementName); private final static TypeName JavaLangStringName = TypeName.string2TypeName("Ljava/lang/String"); public final static TypeReference JavaLangString = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringName); private final static TypeName JavaLangStringBufferName = TypeName.string2TypeName("Ljava/lang/StringBuffer"); public final static TypeReference JavaLangStringBuffer = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringBufferName); private final static TypeName JavaLangStringBuilderName = TypeName.string2TypeName("Ljava/lang/StringBuilder"); public final static TypeReference JavaLangStringBuilder = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringBuilderName); private final static TypeName JavaLangThreadName = TypeName.string2TypeName("Ljava/lang/Thread"); public final static TypeReference JavaLangThread = findOrCreate(ClassLoaderReference.Primordial, JavaLangThreadName); private final static TypeName JavaLangThrowableName = TypeName.string2TypeName("Ljava/lang/Throwable"); public final static TypeReference JavaLangThrowable = findOrCreate(ClassLoaderReference.Primordial, JavaLangThrowableName); public final static TypeName JavaLangCloneableName = TypeName.string2TypeName("Ljava/lang/Cloneable"); public final static TypeReference JavaLangCloneable = findOrCreate(ClassLoaderReference.Primordial, JavaLangCloneableName); private final static TypeName JavaLangSystemName = TypeName.string2TypeName("Ljava/lang/System"); public final static TypeReference JavaLangSystem = findOrCreate(ClassLoaderReference.Primordial, JavaLangSystemName); private final static TypeName JavaLangIntegerName = TypeName.string2TypeName("Ljava/lang/Integer"); public final static TypeReference JavaLangInteger = findOrCreate(ClassLoaderReference.Primordial, JavaLangIntegerName); private final static TypeName JavaLangBooleanName = TypeName.string2TypeName("Ljava/lang/Boolean"); public final static TypeReference JavaLangBoolean = findOrCreate(ClassLoaderReference.Primordial, JavaLangBooleanName); private final static TypeName JavaLangDoubleName = TypeName.string2TypeName("Ljava/lang/Double"); public final static TypeReference JavaLangDouble = findOrCreate(ClassLoaderReference.Primordial, JavaLangDoubleName); private final static TypeName JavaLangFloatName = TypeName.string2TypeName("Ljava/lang/Float"); public final static TypeReference JavaLangFloat = findOrCreate(ClassLoaderReference.Primordial, JavaLangFloatName); private final static TypeName JavaLangShortName = TypeName.string2TypeName("Ljava/lang/Short"); public final static TypeReference JavaLangShort = findOrCreate(ClassLoaderReference.Primordial, JavaLangShortName); private final static TypeName JavaLangLongName = TypeName.string2TypeName("Ljava/lang/Long"); public final static TypeReference JavaLangLong = findOrCreate(ClassLoaderReference.Primordial, JavaLangLongName); private final static TypeName JavaLangByteName = TypeName.string2TypeName("Ljava/lang/Byte"); public final static TypeReference JavaLangByte = findOrCreate(ClassLoaderReference.Primordial, JavaLangByteName); private final static TypeName JavaLangCharacterName = TypeName.string2TypeName("Ljava/lang/Character"); public final static TypeReference JavaLangCharacter = findOrCreate(ClassLoaderReference.Primordial, JavaLangCharacterName); public final static TypeName JavaIoSerializableName = TypeName.string2TypeName("Ljava/io/Serializable"); public final static TypeReference JavaIoSerializable = findOrCreate(ClassLoaderReference.Primordial, JavaIoSerializableName); private final static TypeName JavaUtilCollectionName = TypeName.string2TypeName("Ljava/util/Collection"); public final static TypeReference JavaUtilCollection = findOrCreate(ClassLoaderReference.Primordial, JavaUtilCollectionName); private final static TypeName JavaUtilMapName = TypeName.string2TypeName("Ljava/util/Map"); public final static TypeReference JavaUtilMap = findOrCreate(ClassLoaderReference.Primordial, JavaUtilMapName); private final static TypeName JavaUtilHashSetName = TypeName.string2TypeName("Ljava/util/HashSet"); public final static TypeReference JavaUtilHashSet = findOrCreate(ClassLoaderReference.Primordial, JavaUtilHashSetName); private final static TypeName JavaUtilSetName = TypeName.string2TypeName("Ljava/util/Set"); public final static TypeReference JavaUtilSet = findOrCreate(ClassLoaderReference.Primordial, JavaUtilSetName); private final static TypeName JavaUtilEnumName = TypeName.string2TypeName("Ljava/util/Enumeration"); public final static TypeReference JavaUtilEnum = findOrCreate(ClassLoaderReference.Primordial, JavaUtilEnumName); private final static TypeName JavaUtilIteratorName = TypeName.string2TypeName("Ljava/util/Iterator"); public final static TypeReference JavaUtilIterator = findOrCreate(ClassLoaderReference.Primordial, JavaUtilIteratorName); return name.hashCode(); private final static TypeName JavaUtilVectorName = TypeName.string2TypeName("Ljava/util/Vector"); public final static TypeReference JavaUtilVector = findOrCreate(ClassLoaderReference.Primordial, JavaUtilVectorName); public final static byte ClassTypeCode = 'L'; public final static byte ArrayTypeCode = '['; public final static byte PointerTypeCode = '*'; public final static byte ReferenceTypeCode = '&'; // TODO! the following two are unsound hacks; kill them. final static TypeName NullName = TypeName.string2TypeName("null"); public final static TypeReference Null = findOrCreate(ClassLoaderReference.Primordial, NullName); // TODO: is the following necessary. Used only by ShrikeBT. final static TypeName UnknownName = TypeName.string2TypeName("?unknown?"); public final static TypeReference Unknown = findOrCreate(ClassLoaderReference.Primordial, UnknownName); private static TypeReference makePrimitive(TypeName n) { return makePrimitive(ClassLoaderReference.Primordial, n); } public static TypeReference makePrimitive(ClassLoaderReference cl, TypeName n) { TypeReference t = new TypeReference(cl, n); primitiveMap.put(t.name, t); return t; } /** * Could name a represent a primitive type? */ public static boolean isPrimitiveType(TypeName name) { return name.isPrimitiveType(); } /** * The initiating class loader */ /** private final TypeName name; /** * Find or create the canonical TypeReference instance for the given pair. * * @param cl the classloader (defining/initiating depending on usage) */ public static synchronized TypeReference findOrCreate(ClassLoaderReference cl, TypeName typeName) { if (cl == null) { throw new IllegalArgumentException("null cl"); } TypeReference p = primitiveMap.get(typeName); if (p != null) { return p; } // Next actually findOrCreate the type reference using the proper // classloader. // [This is the only allocation site for TypeReference] if (typeName.isArrayType()) { TypeName e = typeName.getInnermostElementType(); if (e.isPrimitiveType()) { cl = ClassLoaderReference.Primordial; } } Key key = new Key(cl, typeName); TypeReference val = dictionary.get(key); if (val != null) { return val; } else { val = new TypeReference(cl, typeName); dictionary.put(key, val); return val; } } /** * Find or create the canonical {@link TypeReference} instance for the given pair. * * @param cl the classloader (defining/initiating depending on usage) * @param typeName something like "Ljava/util/Arrays" */ public static synchronized TypeReference findOrCreate(ClassLoaderReference cl, String typeName) { return findOrCreate(cl, TypeName.string2TypeName(typeName)); } public static TypeReference findOrCreateArrayOf(TypeReference t) { if (t == null) { throw new IllegalArgumentException("t is null"); } TypeName name = t.getName(); if (t.isPrimitiveType()) { return findOrCreate(ClassLoaderReference.Primordial, name.getArrayTypeForElementType()); } else { return findOrCreate(t.getClassLoader(), name.getArrayTypeForElementType()); } } public static TypeReference findOrCreateReferenceTo(TypeReference t) { if (t == null) { throw new IllegalArgumentException("t is null"); } TypeName name = t.getName(); if (t.isPrimitiveType()) { return findOrCreate(ClassLoaderReference.Primordial, name.getReferenceTypeForElementType()); } else { return findOrCreate(t.getClassLoader(), name.getReferenceTypeForElementType()); } } public static TypeReference findOrCreatePointerTo(TypeReference t) { if (t == null) { throw new IllegalArgumentException("t is null"); } TypeName name = t.getName(); if (t.isPrimitiveType()) { return findOrCreate(ClassLoaderReference.Primordial, name.getPointerTypeForElementType()); } else { return findOrCreate(t.getClassLoader(), name.getPointerTypeForElementType()); } } /** * NB: All type names should use '/' and not '.' as a separator. eg. Ljava/lang/Class * * @param cl the classloader * @param tn the type name */ protected TypeReference(ClassLoaderReference cl, TypeName tn) { classloader = cl; name = tn; } /** * @return the classloader component of this type reference */ public final ClassLoaderReference getClassLoader() { return classloader; } /** * @return the type name component of this type reference */ public final TypeName getName() { return name; } /** * TODO: specialized form of TypeReference for arrays, please. Get the element type of for this array type. */ public final TypeReference getArrayElementType() { TypeName element = name.parseForArrayElementName(); return findOrCreate(classloader, element); } /** * Get array type corresponding to "this" array element type. */ public final TypeReference getArrayTypeForElementType() { return findOrCreate(classloader, name.getArrayTypeForElementType()); } /** * Return the dimensionality of the type. By convention, class types have dimensionality 0, primitives -1, and arrays * the number of [ in their descriptor. */ public final int getDerivedMask() { return name.getDerivedMask(); } /** * Return the innermost element type reference for an array */ public final TypeReference getInnermostElementType() { return findOrCreate(classloader, name.getInnermostElementType()); } /** * Does 'this' refer to a class? */ public final boolean isClassType() { return !isArrayType() && !isPrimitiveType(); } /** * Does 'this' refer to an array? */ public final boolean isArrayType() { return name.isArrayType(); } /** * Does 'this' refer to a primitive type */ public final boolean isPrimitiveType() { return isPrimitiveType(name); } /** * Does 'this' refer to a reference type */ public final boolean isReferenceType() { return !isPrimitiveType(); } @Override public final int hashCode() { /** * TypeReferences are canonical. However, note that two TypeReferences can be non-equal, yet still represent the same * IClass. * * For example, the there can be two TypeReferences and . * These two TypeReference are NOT equal(), but they both represent the IClass which is named * */ @Override public final boolean equals(Object other) { return (this == other); } @Override public final String toString() { return "<" + classloader.getName() + "," + name + ">"; } public static TypeReference findOrCreateClass(ClassLoaderReference loader, String packageName, String className) { TypeName tn = TypeName.findOrCreateClassName(packageName, className); return findOrCreate(loader, tn); } private static class Key { /** * The initiating class loader */ private final ClassLoaderReference classloader; /** * The type name */ private final TypeName name; Key(ClassLoaderReference classloader, TypeName name) { this.classloader = classloader; this.name = name; } @Override public final int hashCode() { return name.hashCode(); } public final boolean equals(Object other) { assert other != null && other instanceof Key; Key that = (Key) other; return (name.equals(that.name) && classloader.equals(that.classloader)); } } public int getDimensionality() { assert isArrayType(); int mask = getDerivedMask(); if ((mask&PrimitiveMask) == PrimitiveMask) { mask >>= ElementBits; } int dims = 0; while ((mask&ArrayMask) == ArrayMask) { mask >>= ElementBits; dims++; } assert dims>0; return dims; } } >>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.types;

import static com.ibm.wala.types.TypeName.ArrayMask;
import static com.ibm.wala.types.TypeName.ElementBits;
import static com.ibm.wala.types.TypeName.PrimitiveMask;

import java.io.Serializable;
import java.util.Map;

import com.ibm.wala.util.collections.HashMapFactory;

/**
 * A class to represent the reference in a class file to some type (class, primitive or array). A type reference is
 * uniquely defined by
 * 
    *
  • an initiating class loader *
  • a type name *
* Resolving a TypeReference to a Type can be an expensive operation. Therefore we canonicalize TypeReference instances * and cache the result of resolution. */ public final class TypeReference implements Serializable { /* Serial version */ private static final long serialVersionUID = -3256390509887654327L; /** * NOTE: initialisation order is important! * * TypeReferences are canonical. */ /** * Used for fast access to primitives. Primitives appear in the main dictionary also. */ private final static Map primitiveMap = HashMapFactory.make(); /** * Used to canonicalize TypeReferences. */ private final static Map dictionary = HashMapFactory.make(); /********************************************************************************************************************* * Primitive Dispatch * ********************************************************************************************************************/ public final static TypeName BooleanName = TypeName.string2TypeName("Z"); public final static byte BooleanTypeCode = 'Z'; public final static TypeReference Boolean = makePrimitive(BooleanName); public final static TypeName ByteName = TypeName.string2TypeName("B"); public final static byte ByteTypeCode = 'B'; public final static TypeReference Byte = makePrimitive(ByteName); public final static TypeName CharName = TypeName.string2TypeName("C"); public final static byte CharTypeCode = 'C'; public final static TypeReference Char = makePrimitive(CharName); public final static TypeName DoubleName = TypeName.string2TypeName("D"); public final static byte DoubleTypeCode = 'D'; public final static TypeReference Double = makePrimitive(DoubleName); public final static TypeName FloatName = TypeName.string2TypeName("F"); public final static byte FloatTypeCode = 'F'; public final static TypeReference Float = makePrimitive(FloatName); public final static TypeName IntName = TypeName.string2TypeName("I"); public final static byte IntTypeCode = 'I'; public final static TypeReference Int = makePrimitive(IntName); public final static TypeName LongName = TypeName.string2TypeName("J"); public final static byte LongTypeCode = 'J'; public final static TypeReference Long = makePrimitive(LongName); public final static TypeName ShortName = TypeName.string2TypeName("S"); public final static byte ShortTypeCode = 'S'; public final static TypeReference Short = makePrimitive(ShortName); public final static TypeName VoidName = TypeName.string2TypeName("V"); public final static byte VoidTypeCode = 'V'; public final static TypeReference Void = makePrimitive(VoidName); public final static byte OtherPrimitiveTypeCode = 'P'; /********************************************************************************************************************* * Primitive Array Dispatch * ********************************************************************************************************************/ public final static TypeReference BooleanArray = findOrCreateArrayOf(Boolean); public final static TypeReference ByteArray = findOrCreateArrayOf(Byte); public final static TypeReference CharArray = findOrCreateArrayOf(Char); public final static TypeReference DoubleArray = findOrCreateArrayOf(Double); public final static TypeReference FloatArray = findOrCreateArrayOf(Float); public final static TypeReference IntArray = findOrCreateArrayOf(Int); if (cl == null) { public final static TypeReference LongArray = findOrCreateArrayOf(Long); public final static TypeReference ShortArray = findOrCreateArrayOf(Short); /********************************************************************************************************************* * Special object types * ********************************************************************************************************************/ private final static TypeName JavaLangArithmeticExceptionName = TypeName.string2TypeName("Ljava/lang/ArithmeticException"); public final static TypeReference JavaLangArithmeticException = findOrCreate(ClassLoaderReference.Primordial, JavaLangArithmeticExceptionName); private final static TypeName JavaLangArrayStoreExceptionName = TypeName.string2TypeName("Ljava/lang/ArrayStoreException"); public final static TypeReference JavaLangArrayStoreException = findOrCreate(ClassLoaderReference.Primordial, JavaLangArrayStoreExceptionName); private final static TypeName JavaLangArrayIndexOutOfBoundsExceptionName = TypeName .string2TypeName("Ljava/lang/ArrayIndexOutOfBoundsException"); public final static TypeReference JavaLangArrayIndexOutOfBoundsException = findOrCreate(ClassLoaderReference.Primordial, JavaLangArrayIndexOutOfBoundsExceptionName); private final static TypeName JavaLangClassName = TypeName.string2TypeName("Ljava/lang/Class"); public final static TypeReference JavaLangClass = findOrCreate(ClassLoaderReference.Primordial, JavaLangClassName); private final static TypeName JavaLangClassCastExceptionName = TypeName.string2TypeName("Ljava/lang/ClassCastException"); public final static TypeReference JavaLangClassCastException = findOrCreate(ClassLoaderReference.Primordial, JavaLangClassCastExceptionName); private final static TypeName JavaLangComparableName = TypeName.string2TypeName("Ljava/lang/Comparable"); public final static TypeReference JavaLangComparable = findOrCreate(ClassLoaderReference.Primordial, JavaLangComparableName); private final static TypeName JavaLangReflectConstructorName = TypeName.string2TypeName("Ljava/lang/reflect/Constructor"); public final static TypeReference JavaLangReflectConstructor = findOrCreate(ClassLoaderReference.Primordial, JavaLangReflectConstructorName); private final static TypeName JavaLangReflectMethodName = TypeName.string2TypeName("Ljava/lang/reflect/Method"); public final static TypeReference JavaLangReflectMethod = findOrCreate(ClassLoaderReference.Primordial, JavaLangReflectMethodName); private final static TypeName JavaLangEnumName = TypeName.string2TypeName("Ljava/lang/Enum"); public final static TypeReference JavaLangEnum = findOrCreate(ClassLoaderReference.Primordial, JavaLangEnumName); private final static TypeName JavaLangErrorName = TypeName.string2TypeName("Ljava/lang/Error"); public final static TypeReference JavaLangError = findOrCreate(ClassLoaderReference.Primordial, JavaLangErrorName); private final static TypeName JavaLangExceptionName = TypeName.string2TypeName("Ljava/lang/Exception"); public final static TypeReference JavaLangException = findOrCreate(ClassLoaderReference.Primordial, JavaLangExceptionName); private final static TypeName JavaLangNegativeArraySizeExceptionName = TypeName .string2TypeName("Ljava/lang/NegativeArraySizeException"); public final static TypeReference JavaLangNegativeArraySizeException = findOrCreate(ClassLoaderReference.Primordial, JavaLangNegativeArraySizeExceptionName); private final static TypeName JavaLangNullPointerExceptionName = TypeName.string2TypeName("Ljava/lang/NullPointerException"); public final static TypeReference JavaLangNullPointerException = findOrCreate(ClassLoaderReference.Primordial, JavaLangNullPointerExceptionName); private final static TypeName JavaLangRuntimeExceptionName = TypeName.string2TypeName("Ljava/lang/RuntimeException"); public final static TypeReference JavaLangRuntimeException = findOrCreate(ClassLoaderReference.Primordial, JavaLangRuntimeExceptionName); private final static TypeName JavaLangClassNotFoundExceptionName = TypeName.string2TypeName("Ljava/lang/ClassNotFoundException"); throw new IllegalArgumentException("null cl"); public final static TypeReference JavaLangClassNotFoundException = findOrCreate(ClassLoaderReference.Primordial, JavaLangClassNotFoundExceptionName); private final static TypeName JavaLangOutOfMemoryErrorName = TypeName.string2TypeName("Ljava/lang/OutOfMemoryError"); public final static TypeReference JavaLangOutOfMemoryError = findOrCreate(ClassLoaderReference.Primordial, JavaLangOutOfMemoryErrorName); private final static TypeName JavaLangExceptionInInitializerErrorName = TypeName .string2TypeName("Ljava/lang/ExceptionInInitializerError"); public final static TypeReference JavaLangExceptionInInitializerError = findOrCreate(ClassLoaderReference.Primordial, JavaLangExceptionInInitializerErrorName); private final static TypeName JavaLangObjectName = TypeName.string2TypeName("Ljava/lang/Object"); public final static TypeReference JavaLangObject = findOrCreate(ClassLoaderReference.Primordial, JavaLangObjectName); private final static TypeName JavaLangStackTraceElementName = TypeName.string2TypeName("Ljava/lang/StackTraceElement"); public final static TypeReference JavaLangStackTraceElement = findOrCreate(ClassLoaderReference.Primordial, JavaLangStackTraceElementName); private final static TypeName JavaLangStringName = TypeName.string2TypeName("Ljava/lang/String"); public final static TypeReference JavaLangString = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringName); private final static TypeName JavaLangStringBufferName = TypeName.string2TypeName("Ljava/lang/StringBuffer"); public final static TypeReference JavaLangStringBuffer = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringBufferName); private final static TypeName JavaLangStringBuilderName = TypeName.string2TypeName("Ljava/lang/StringBuilder"); public final static TypeReference JavaLangStringBuilder = findOrCreate(ClassLoaderReference.Primordial, JavaLangStringBuilderName); private final static TypeName JavaLangThreadName = TypeName.string2TypeName("Ljava/lang/Thread"); public final static TypeReference JavaLangThread = findOrCreate(ClassLoaderReference.Primordial, JavaLangThreadName); private final static TypeName JavaLangThrowableName = TypeName.string2TypeName("Ljava/lang/Throwable"); public final static TypeReference JavaLangThrowable = findOrCreate(ClassLoaderReference.Primordial, JavaLangThrowableName); public final static TypeName JavaLangCloneableName = TypeName.string2TypeName("Ljava/lang/Cloneable"); public final static TypeReference JavaLangCloneable = findOrCreate(ClassLoaderReference.Primordial, JavaLangCloneableName); private final static TypeName JavaLangSystemName = TypeName.string2TypeName("Ljava/lang/System"); public final static TypeReference JavaLangSystem = findOrCreate(ClassLoaderReference.Primordial, JavaLangSystemName); private final static TypeName JavaLangIntegerName = TypeName.string2TypeName("Ljava/lang/Integer"); public final static TypeReference JavaLangInteger = findOrCreate(ClassLoaderReference.Primordial, JavaLangIntegerName); private final static TypeName JavaLangBooleanName = TypeName.string2TypeName("Ljava/lang/Boolean"); public final static TypeReference JavaLangBoolean = findOrCreate(ClassLoaderReference.Primordial, JavaLangBooleanName); private final static TypeName JavaLangDoubleName = TypeName.string2TypeName("Ljava/lang/Double"); public final static TypeReference JavaLangDouble = findOrCreate(ClassLoaderReference.Primordial, JavaLangDoubleName); private final static TypeName JavaLangFloatName = TypeName.string2TypeName("Ljava/lang/Float"); public final static TypeReference JavaLangFloat = findOrCreate(ClassLoaderReference.Primordial, JavaLangFloatName); private final static TypeName JavaLangShortName = TypeName.string2TypeName("Ljava/lang/Short"); public final static TypeReference JavaLangShort = findOrCreate(ClassLoaderReference.Primordial, JavaLangShortName); private final static TypeName JavaLangLongName = TypeName.string2TypeName("Ljava/lang/Long"); public final static TypeReference JavaLangLong = findOrCreate(ClassLoaderReference.Primordial, JavaLangLongName); private final static TypeName JavaLangByteName = TypeName.string2TypeName("Ljava/lang/Byte"); } public final static TypeReference JavaLangByte = findOrCreate(ClassLoaderReference.Primordial, JavaLangByteName); private final static TypeName JavaLangCharacterName = TypeName.string2TypeName("Ljava/lang/Character"); public final static TypeReference JavaLangCharacter = findOrCreate(ClassLoaderReference.Primordial, JavaLangCharacterName); public final static TypeName JavaIoSerializableName = TypeName.string2TypeName("Ljava/io/Serializable"); public final static TypeReference JavaIoSerializable = findOrCreate(ClassLoaderReference.Primordial, JavaIoSerializableName); private final static TypeName JavaUtilCollectionName = TypeName.string2TypeName("Ljava/util/Collection"); public final static TypeReference JavaUtilCollection = findOrCreate(ClassLoaderReference.Primordial, JavaUtilCollectionName); private final static TypeName JavaUtilMapName = TypeName.string2TypeName("Ljava/util/Map"); public final static TypeReference JavaUtilMap = findOrCreate(ClassLoaderReference.Primordial, JavaUtilMapName); private final static TypeName JavaUtilHashSetName = TypeName.string2TypeName("Ljava/util/HashSet"); public final static TypeReference JavaUtilHashSet = findOrCreate(ClassLoaderReference.Primordial, JavaUtilHashSetName); private final static TypeName JavaUtilSetName = TypeName.string2TypeName("Ljava/util/Set"); public final static TypeReference JavaUtilSet = findOrCreate(ClassLoaderReference.Primordial, JavaUtilSetName); private final static TypeName JavaUtilEnumName = TypeName.string2TypeName("Ljava/util/Enumeration"); public final static TypeReference JavaUtilEnum = findOrCreate(ClassLoaderReference.Primordial, JavaUtilEnumName); private final static TypeName JavaUtilIteratorName = TypeName.string2TypeName("Ljava/util/Iterator"); public final static TypeReference JavaUtilIterator = findOrCreate(ClassLoaderReference.Primordial, JavaUtilIteratorName); private final static TypeName JavaUtilVectorName = TypeName.string2TypeName("Ljava/util/Vector"); public final static TypeReference JavaUtilVector = findOrCreate(ClassLoaderReference.Primordial, JavaUtilVectorName); public final static byte ClassTypeCode = 'L'; public final static byte ArrayTypeCode = '['; public final static byte PointerTypeCode = '*'; public final static byte ReferenceTypeCode = '&'; // TODO! the following two are unsound hacks; kill them. final static TypeName NullName = TypeName.string2TypeName("null"); public final static TypeReference Null = findOrCreate(ClassLoaderReference.Primordial, NullName); // TODO: is the following necessary. Used only by ShrikeBT. final static TypeName UnknownName = TypeName.string2TypeName("?unknown?"); public final static TypeReference Unknown = findOrCreate(ClassLoaderReference.Primordial, UnknownName); private static TypeReference makePrimitive(TypeName n) { return makePrimitive(ClassLoaderReference.Primordial, n); } public static TypeReference makePrimitive(ClassLoaderReference cl, TypeName n) { TypeReference t = new TypeReference(cl, n); primitiveMap.put(t.name, t); return t; } /** * Could name a represent a primitive type? */ public static boolean isPrimitiveType(TypeName name) { return name.isPrimitiveType(); } /** * The initiating class loader */ private final ClassLoaderReference classloader; /** * The type name */ private final TypeName name; /** * Find or create the canonical TypeReference instance for the given pair. * * @param cl the classloader (defining/initiating depending on usage) */ public static synchronized TypeReference findOrCreate(ClassLoaderReference cl, TypeName typeName) { TypeReference p = primitiveMap.get(typeName); if (p != null) { return p; } // Next actually findOrCreate the type reference using the proper // classloader. // [This is the only allocation site for TypeReference] if (typeName.isArrayType()) { TypeName e = typeName.getInnermostElementType(); if (e.isPrimitiveType()) { cl = ClassLoaderReference.Primordial; } } Key key = new Key(cl, typeName); TypeReference val = dictionary.get(key); if (val != null) { return val; } else { val = new TypeReference(cl, typeName); dictionary.put(key, val); return val; } } /** * Find or create the canonical {@link TypeReference} instance for the given pair. * * @param cl the classloader (defining/initiating depending on usage) * @param typeName something like "Ljava/util/Arrays" */ public static synchronized TypeReference findOrCreate(ClassLoaderReference cl, String typeName) { return findOrCreate(cl, TypeName.string2TypeName(typeName)); } /** BEGIN Custom change: search types */ public static synchronized TypeReference find(ClassLoaderReference cl, String typeName) { return find(cl, TypeName.string2TypeName(typeName)); } /** * Find the canonical TypeReference instance for the given pair. May return null. * * @param cl the classloader (defining/initiating depending on usage) */ public static synchronized TypeReference find(ClassLoaderReference cl, TypeName typeName) { if (cl == null) { throw new IllegalArgumentException("null cl"); } TypeReference p = primitiveMap.get(typeName); if (p != null) { return p; } // Next actually findOrCreate the type reference using the proper // classloader. // [This is the only allocation site for TypeReference] if (typeName.isArrayType()) { TypeName e = typeName.getInnermostElementType(); if (e.isPrimitiveType()) { cl = ClassLoaderReference.Primordial; } } Key key = new Key(cl, typeName); TypeReference val = dictionary.get(key); return val; } /** END Custom change: search types */ public static TypeReference findOrCreateArrayOf(TypeReference t) { if (t == null) { throw new IllegalArgumentException("t is null"); } TypeName name = t.getName(); if (t.isPrimitiveType()) { return findOrCreate(ClassLoaderReference.Primordial, name.getArrayTypeForElementType()); } else { return findOrCreate(t.getClassLoader(), name.getArrayTypeForElementType()); } } public static TypeReference findOrCreateReferenceTo(TypeReference t) { if (t == null) { throw new IllegalArgumentException("t is null"); } TypeName name = t.getName(); if (t.isPrimitiveType()) { return findOrCreate(ClassLoaderReference.Primordial, name.getReferenceTypeForElementType()); } else { return findOrCreate(t.getClassLoader(), name.getReferenceTypeForElementType()); } } public static TypeReference findOrCreatePointerTo(TypeReference t) { if (t == null) { throw new IllegalArgumentException("t is null"); } TypeName name = t.getName(); if (t.isPrimitiveType()) { return findOrCreate(ClassLoaderReference.Primordial, name.getPointerTypeForElementType()); } else { return findOrCreate(t.getClassLoader(), name.getPointerTypeForElementType()); } } /** * NB: All type names should use '/' and not '.' as a separator. eg. Ljava/lang/Class * * @param cl the classloader * @param tn the type name */ protected TypeReference(ClassLoaderReference cl, TypeName tn) { classloader = cl; name = tn; } /** * @return the classloader component of this type reference */ public final ClassLoaderReference getClassLoader() { return classloader; } /** * @return the type name component of this type reference */ public final TypeName getName() { return name; } /** * TODO: specialized form of TypeReference for arrays, please. Get the element type of for this array type. */ public final TypeReference getArrayElementType() { TypeName element = name.parseForArrayElementName(); return findOrCreate(classloader, element); } /** * Get array type corresponding to "this" array element type. */ public final TypeReference getArrayTypeForElementType() { return findOrCreate(classloader, name.getArrayTypeForElementType()); } /** * Return the dimensionality of the type. By convention, class types have dimensionality 0, primitives -1, and arrays * the number of [ in their descriptor. */ public final int getDerivedMask() { return name.getDerivedMask(); } /** * Return the innermost element type reference for an array */ public final TypeReference getInnermostElementType() { return findOrCreate(classloader, name.getInnermostElementType()); } /** * Does 'this' refer to a class? */ public final boolean isClassType() { return !isArrayType() && !isPrimitiveType(); } /** * Does 'this' refer to an array? */ public final boolean isArrayType() { return name.isArrayType(); } /** * Does 'this' refer to a primitive type */ public final boolean isPrimitiveType() { return isPrimitiveType(name); } /** * Does 'this' refer to a reference type */ public final boolean isReferenceType() { return !isPrimitiveType(); } @Override public final int hashCode() { return name.hashCode(); } /** * TypeReferences are canonical. However, note that two TypeReferences can be non-equal, yet still represent the same * IClass. * * For example, the there can be two TypeReferences and . * These two TypeReference are NOT equal(), but they both represent the IClass which is named * */ @Override public final boolean equals(Object other) { return (this == other); } @Override public final String toString() { return "<" + classloader.getName() + "," + name + ">"; } public static TypeReference findOrCreateClass(ClassLoaderReference loader, String packageName, String className) { TypeName tn = TypeName.findOrCreateClassName(packageName, className); return findOrCreate(loader, tn); } private static class Key { /** * The initiating class loader */ private final ClassLoaderReference classloader; /** * The type name */ private final TypeName name; Key(ClassLoaderReference classloader, TypeName name) { this.classloader = classloader; this.name = name; } @Override public final int hashCode() { return name.hashCode(); } @Override public final boolean equals(Object other) { assert other != null && other instanceof Key; Key that = (Key) other; return (name.equals(that.name) && classloader.equals(that.classloader)); } } public int getDimensionality() { assert isArrayType(); int mask = getDerivedMask(); if ((mask&PrimitiveMask) == PrimitiveMask) { mask >>= ElementBits; } int dims = 0; while ((mask&ArrayMask) == ArrayMask) { mask >>= ElementBits; dims++; } assert dims>0; return dims; } }
File
TypeReference.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2007 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.util.config;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.jar.JarFile;

import com.ibm.wala.classLoader.BinaryDirectoryTreeModule;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.SourceDirectoryTreeModule;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.properties.WalaProperties;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.io.FileProvider;
import com.ibm.wala.util.strings.Atom;

/**
 * Reads {@link AnalysisScope} from a text file.
 */
public class AnalysisScopeReader {

  private static final ClassLoader MY_CLASSLOADER = AnalysisScopeReader.class.getClassLoader();

  protected static final String BASIC_FILE = "primordial.txt";

  /**
   * read in an analysis scope for a Java application from a text file
   * @param scopeFileName the text file specifying the scope
   * @param exclusionsFile a file specifying code to be excluded from the scope; can be null
   * @param javaLoader the class loader used to read in files referenced in the scope file, via {@link ClassLoader#getResource(String)}
   * @return the analysis scope
   * @throws IOException
   */
  public static AnalysisScope readJavaScope(String scopeFileName, File exclusionsFile, ClassLoader javaLoader) throws IOException {
    AnalysisScope scope = AnalysisScope.createJavaAnalysisScope();
    return read(scope, scopeFileName, exclusionsFile, javaLoader, new FileProvider());
  }


  protected static AnalysisScope read(AnalysisScope scope, String scopeFileName, File exclusionsFile, ClassLoader javaLoader,
      FileProvider fp) throws IOException {
    BufferedReader r = null;
    try {
      File scopeFile = fp.getFile(scopeFileName, javaLoader);
      assert scopeFile.exists();

      String line;
      // assume the scope file is UTF-8 encoded; ASCII files will also be handled properly
      // TODO allow specifying encoding as a parameter?
/** BEGIN Custom change: try to load from jar as fallback */
      if (scopeFile.exists()) {
        r = new BufferedReader(new InputStreamReader(new FileInputStream(scopeFile), "UTF-8"));
      } else {
        // try to read from jar
        InputStream inFromJar = scope.getClass().getClassLoader().getResourceAsStream(scopeFileName);
        r = new BufferedReader(new InputStreamReader(inFromJar));
      }
/** END Custom change: try to load from jar as fallback */
      while ((line = r.readLine()) != null) {
        processScopeDefLine(scope, javaLoader, line);
      }

      if (exclusionsFile != null) {
        scope.setExclusions(FileOfClasses.createFileOfClasses(exclusionsFile));
      }

    } finally {
      if (r != null) {
        try {
          r.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

    return scope;
  }

  public static void processScopeDefLine(AnalysisScope scope, ClassLoader javaLoader, String line) throws IOException {
    if (line == null) {
      throw new IllegalArgumentException("null line");
    }
    StringTokenizer toks = new StringTokenizer(line, "\n,");
    if (!toks.hasMoreTokens()) {
      return;
    }
    Atom loaderName = Atom.findOrCreateUnicodeAtom(toks.nextToken());
    ClassLoaderReference walaLoader = scope.getLoader(loaderName);

    @SuppressWarnings("unused")
    String language = toks.nextToken();
    String entryType = toks.nextToken();
    String entryPathname = toks.nextToken();
    FileProvider fp = (new FileProvider());
    if ("classFile".equals(entryType)) {
      File cf = fp.getFile(entryPathname, javaLoader);
      try {
        scope.addClassFileToScope(walaLoader, cf);
      } catch (InvalidClassFileException e) {
        Assertions.UNREACHABLE(e.toString());
      }
    } else if ("sourceFile".equals(entryType)) {
      File sf = fp.getFile(entryPathname, javaLoader);
      scope.addSourceFileToScope(walaLoader, sf, entryPathname);
    } else if ("binaryDir".equals(entryType)) {
      File bd = fp.getFile(entryPathname, javaLoader);
      assert bd.isDirectory();
      scope.addToScope(walaLoader, new BinaryDirectoryTreeModule(bd));
    } else if ("sourceDir".equals(entryType)) {
      File sd = fp.getFile(entryPathname, javaLoader);
      assert sd.isDirectory();
      scope.addToScope(walaLoader, new SourceDirectoryTreeModule(sd));
    } else if ("jarFile".equals(entryType)) {
      Module M = fp.getJarFileModule(entryPathname, javaLoader);
      scope.addToScope(walaLoader, M);
    } else if ("loaderImpl".equals(entryType)) {
      scope.setLoaderImpl(walaLoader, entryPathname);
    } else if ("stdlib".equals(entryType)) {
      String[] stdlibs = WalaProperties.getJ2SEJarFiles();
      for (int i = 0; i < stdlibs.length; i++) {
        scope.addToScope(walaLoader, new JarFile(stdlibs[i]));
      }
    } else {
      Assertions.UNREACHABLE();
    }
  }

  /**
   * @param exclusionsFile file holding class hierarchy exclusions. may be null
   * @throws IOException 
   * @throws IllegalStateException if there are problmes reading wala properties
   */
  public static AnalysisScope makePrimordialScope(File exclusionsFile) throws IOException {
    return readJavaScope(BASIC_FILE, exclusionsFile, MY_CLASSLOADER);
  }



  /**
   * @param classPath class path to analyze, delimited by File.pathSeparator
   * @param exclusionsFile file holding class hierarchy exclusions. may be null
   * @throws IOException 
   * @throws IllegalStateException if there are problems reading wala properties
   */
  public static AnalysisScope makeJavaBinaryAnalysisScope(String classPath, File exclusionsFile) throws IOException {
    if (classPath == null) {
      throw new IllegalArgumentException("classPath null");
    }
    }
    AnalysisScope scope = makePrimordialScope(exclusionsFile);
    ClassLoaderReference loader = scope.getLoader(AnalysisScope.APPLICATION);

    addClassPathToScope(classPath, scope, loader);

    return scope;
  }

  public static void addClassPathToScope(String classPath, AnalysisScope scope, ClassLoaderReference loader) {
    if (classPath == null) {
      throw new IllegalArgumentException("null classPath");
    }
    try {
      StringTokenizer paths = new StringTokenizer(classPath, File.pathSeparator);
      while (paths.hasMoreTokens()) {
        String path = paths.nextToken();
        if (path.endsWith(".jar")) {
          scope.addToScope(loader, new JarFile(path));
        } else {
          File f = new File(path);
          if (f.isDirectory()) {
            scope.addToScope(loader, new BinaryDirectoryTreeModule(f));
          } else {
            scope.addClassFileToScope(loader, f);
          }
        }
      }
    } catch (IOException e) {
      Assertions.UNREACHABLE(e.toString());
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE(e.toString());
    }
  }
}
=======
/*******************************************************************************
 * Copyright (c) 2007 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.util.config;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.jar.JarFile;

import com.ibm.wala.classLoader.BinaryDirectoryTreeModule;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.SourceDirectoryTreeModule;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.properties.WalaProperties;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.io.FileProvider;
import com.ibm.wala.util.strings.Atom;

/**
 * Reads {@link AnalysisScope} from a text file.
 */
public class AnalysisScopeReader {

  private static final ClassLoader MY_CLASSLOADER = AnalysisScopeReader.class.getClassLoader();

  protected static final String BASIC_FILE = "primordial.txt";

  /**
   * read in an analysis scope for a Java application from a text file
   * @param scopeFileName the text file specifying the scope
   * @param exclusionsFile a file specifying code to be excluded from the scope; can be null
   * @param javaLoader the class loader used to read in files referenced in the scope file, via {@link ClassLoader#getResource(String)}
   * @return the analysis scope
   * @throws IOException
   */
  public static AnalysisScope readJavaScope(String scopeFileName, File exclusionsFile, ClassLoader javaLoader) throws IOException {
    AnalysisScope scope = AnalysisScope.createJavaAnalysisScope();
    return read(scope, scopeFileName, exclusionsFile, javaLoader, new FileProvider());
  }


  protected static AnalysisScope read(AnalysisScope scope, String scopeFileName, File exclusionsFile, ClassLoader javaLoader,
      FileProvider fp) throws IOException {
    BufferedReader r = null;
    try {
      File scopeFile = fp.getFile(scopeFileName, javaLoader);
      assert scopeFile.exists();

      String line;
      // assume the scope file is UTF-8 encoded; ASCII files will also be handled properly
      // TODO allow specifying encoding as a parameter?
      r = new BufferedReader(new InputStreamReader(new FileInputStream(scopeFile), "UTF-8"));
      while ((line = r.readLine()) != null) {
        processScopeDefLine(scope, javaLoader, line);
      }

      if (exclusionsFile != null) {
        scope.setExclusions(FileOfClasses.createFileOfClasses(exclusionsFile));
      }

    } finally {
      if (r != null) {
        try {
          r.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

    return scope;
  }

  public static void processScopeDefLine(AnalysisScope scope, ClassLoader javaLoader, String line) throws IOException {
    if (line == null) {
      throw new IllegalArgumentException("null line");
    }
    StringTokenizer toks = new StringTokenizer(line, "\n,");
    if (!toks.hasMoreTokens()) {
      return;
    }
    Atom loaderName = Atom.findOrCreateUnicodeAtom(toks.nextToken());
    ClassLoaderReference walaLoader = scope.getLoader(loaderName);

    @SuppressWarnings("unused")
    String language = toks.nextToken();
    String entryType = toks.nextToken();
    String entryPathname = toks.nextToken();
    FileProvider fp = (new FileProvider());
    if ("classFile".equals(entryType)) {
      File cf = fp.getFile(entryPathname, javaLoader);
      try {
        scope.addClassFileToScope(walaLoader, cf);
      } catch (InvalidClassFileException e) {
        Assertions.UNREACHABLE(e.toString());
      }
    } else if ("sourceFile".equals(entryType)) {
      File sf = fp.getFile(entryPathname, javaLoader);
      scope.addSourceFileToScope(walaLoader, sf, entryPathname);
    } else if ("binaryDir".equals(entryType)) {
      File bd = fp.getFile(entryPathname, javaLoader);
      assert bd.isDirectory();
      scope.addToScope(walaLoader, new BinaryDirectoryTreeModule(bd));
    } else if ("sourceDir".equals(entryType)) {
      File sd = fp.getFile(entryPathname, javaLoader);
      assert sd.isDirectory();
      scope.addToScope(walaLoader, new SourceDirectoryTreeModule(sd));
    } else if ("jarFile".equals(entryType)) {
      Module M = fp.getJarFileModule(entryPathname, javaLoader);
      scope.addToScope(walaLoader, M);
    } else if ("loaderImpl".equals(entryType)) {
      scope.setLoaderImpl(walaLoader, entryPathname);
    } else if ("stdlib".equals(entryType)) {
      String[] stdlibs = WalaProperties.getJ2SEJarFiles();
      for (int i = 0; i < stdlibs.length; i++) {
        scope.addToScope(walaLoader, new JarFile(stdlibs[i]));
      }
    } else {
      Assertions.UNREACHABLE();
    }
  }

  /**
   * @param exclusionsFile file holding class hierarchy exclusions. may be null
   * @throws IOException 
   * @throws IllegalStateException if there are problmes reading wala properties
   */
  public static AnalysisScope makePrimordialScope(File exclusionsFile) throws IOException {
    return readJavaScope(BASIC_FILE, exclusionsFile, MY_CLASSLOADER);
  }



  /**
   * @param classPath class path to analyze, delimited by File.pathSeparator
   * @param exclusionsFile file holding class hierarchy exclusions. may be null
   * @throws IOException 
   * @throws IllegalStateException if there are problems reading wala properties
   */
  public static AnalysisScope makeJavaBinaryAnalysisScope(String classPath, File exclusionsFile) throws IOException {
    if (classPath == null) {
      throw new IllegalArgumentException("classPath null");
    AnalysisScope scope = makePrimordialScope(exclusionsFile);
    ClassLoaderReference loader = scope.getLoader(AnalysisScope.APPLICATION);

    addClassPathToScope(classPath, scope, loader);

    return scope;
  }

  public static void addClassPathToScope(String classPath, AnalysisScope scope, ClassLoaderReference loader) {
    if (classPath == null) {
      throw new IllegalArgumentException("null classPath");
    }
    try {
      StringTokenizer paths = new StringTokenizer(classPath, File.pathSeparator);
      while (paths.hasMoreTokens()) {
        String path = paths.nextToken();
        if (path.endsWith(".jar")) {
          scope.addToScope(loader, new JarFile(path));
        } else {
          File f = new File(path);
          if (f.isDirectory()) {
            scope.addToScope(loader, new BinaryDirectoryTreeModule(f));
          } else {
            scope.addClassFileToScope(loader, f);
          }
        }
      }
    } catch (IOException e) {
      Assertions.UNREACHABLE(e.toString());
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE(e.toString());
    }
  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2007 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.util.config;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.jar.JarFile;

import com.ibm.wala.classLoader.BinaryDirectoryTreeModule;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.SourceDirectoryTreeModule;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.properties.WalaProperties;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.io.FileProvider;
import com.ibm.wala.util.strings.Atom;

/**
 * Reads {@link AnalysisScope} from a text file.
 */
public class AnalysisScopeReader {

  private static final ClassLoader MY_CLASSLOADER = AnalysisScopeReader.class.getClassLoader();

  protected static final String BASIC_FILE = "primordial.txt";

  /**
   * read in an analysis scope for a Java application from a text file
   * @param scopeFileName the text file specifying the scope
   * @param exclusionsFile a file specifying code to be excluded from the scope; can be null
   * @param javaLoader the class loader used to read in files referenced in the scope file, via {@link ClassLoader#getResource(String)}
   * @return the analysis scope
   * @throws IOException
   */
  /**
   * @param exclusionsFile file holding class hierarchy exclusions. may be null
  public static AnalysisScope readJavaScope(String scopeFileName, File exclusionsFile, ClassLoader javaLoader) throws IOException {
    AnalysisScope scope = AnalysisScope.createJavaAnalysisScope();
    return read(scope, scopeFileName, exclusionsFile, javaLoader, new FileProvider());
  }


  protected static AnalysisScope read(AnalysisScope scope, String scopeFileName, File exclusionsFile, ClassLoader javaLoader,
      FileProvider fp) throws IOException {
    BufferedReader r = null;
    try {
      File scopeFile = fp.getFile(scopeFileName, javaLoader);
      assert scopeFile.exists();

      String line;
      // assume the scope file is UTF-8 encoded; ASCII files will also be handled properly
      // TODO allow specifying encoding as a parameter?
/** BEGIN Custom change: try to load from jar as fallback */
      if (scopeFile.exists()) {
        r = new BufferedReader(new InputStreamReader(new FileInputStream(scopeFile), "UTF-8"));
      } else {
        // try to read from jar
        InputStream inFromJar = scope.getClass().getClassLoader().getResourceAsStream(scopeFileName);
        r = new BufferedReader(new InputStreamReader(inFromJar));
      }
/** END Custom change: try to load from jar as fallback */
      while ((line = r.readLine()) != null) {
        processScopeDefLine(scope, javaLoader, line);
      }

      if (exclusionsFile != null) {
        scope.setExclusions(FileOfClasses.createFileOfClasses(exclusionsFile));
      }

    } finally {
      if (r != null) {
        try {
          r.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

    return scope;
  }

  public static void processScopeDefLine(AnalysisScope scope, ClassLoader javaLoader, String line) throws IOException {
    if (line == null) {
      throw new IllegalArgumentException("null line");
    }
    StringTokenizer toks = new StringTokenizer(line, "\n,");
    if (!toks.hasMoreTokens()) {
      return;
    }
    Atom loaderName = Atom.findOrCreateUnicodeAtom(toks.nextToken());
    ClassLoaderReference walaLoader = scope.getLoader(loaderName);

    @SuppressWarnings("unused")
    String language = toks.nextToken();
    String entryType = toks.nextToken();
    String entryPathname = toks.nextToken();
    FileProvider fp = (new FileProvider());
    if ("classFile".equals(entryType)) {
      File cf = fp.getFile(entryPathname, javaLoader);
      try {
        scope.addClassFileToScope(walaLoader, cf);
      } catch (InvalidClassFileException e) {
        Assertions.UNREACHABLE(e.toString());
      }
    } else if ("sourceFile".equals(entryType)) {
      File sf = fp.getFile(entryPathname, javaLoader);
      scope.addSourceFileToScope(walaLoader, sf, entryPathname);
    } else if ("binaryDir".equals(entryType)) {
      File bd = fp.getFile(entryPathname, javaLoader);
      assert bd.isDirectory();
      scope.addToScope(walaLoader, new BinaryDirectoryTreeModule(bd));
    } else if ("sourceDir".equals(entryType)) {
      File sd = fp.getFile(entryPathname, javaLoader);
      assert sd.isDirectory();
      scope.addToScope(walaLoader, new SourceDirectoryTreeModule(sd));
    } else if ("jarFile".equals(entryType)) {
      Module M = fp.getJarFileModule(entryPathname, javaLoader);
      scope.addToScope(walaLoader, M);
    } else if ("loaderImpl".equals(entryType)) {
      scope.setLoaderImpl(walaLoader, entryPathname);
    } else if ("stdlib".equals(entryType)) {
      String[] stdlibs = WalaProperties.getJ2SEJarFiles();
      for (int i = 0; i < stdlibs.length; i++) {
        scope.addToScope(walaLoader, new JarFile(stdlibs[i]));
      }
    } else {
      Assertions.UNREACHABLE();
    }
  }

   * @throws IOException 
   * @throws IllegalStateException if there are problmes reading wala properties
   */
  public static AnalysisScope makePrimordialScope(File exclusionsFile) throws IOException {
    return readJavaScope(BASIC_FILE, exclusionsFile, MY_CLASSLOADER);
  }



  /**
   * @param classPath class path to analyze, delimited by File.pathSeparator
   * @param exclusionsFile file holding class hierarchy exclusions. may be null
   * @throws IOException 
   * @throws IllegalStateException if there are problems reading wala properties
   */
  public static AnalysisScope makeJavaBinaryAnalysisScope(String classPath, File exclusionsFile) throws IOException {
    if (classPath == null) {
      throw new IllegalArgumentException("classPath null");
    }
    AnalysisScope scope = makePrimordialScope(exclusionsFile);
    ClassLoaderReference loader = scope.getLoader(AnalysisScope.APPLICATION);

    addClassPathToScope(classPath, scope, loader);

    return scope;
  }

  public static void addClassPathToScope(String classPath, AnalysisScope scope, ClassLoaderReference loader) {
    if (classPath == null) {
      throw new IllegalArgumentException("null classPath");
    }
    try {
      StringTokenizer paths = new StringTokenizer(classPath, File.pathSeparator);
      while (paths.hasMoreTokens()) {
        String path = paths.nextToken();
        if (path.endsWith(".jar")) {
          scope.addToScope(loader, new JarFile(path));
        } else {
          File f = new File(path);
          if (f.isDirectory()) {
            scope.addToScope(loader, new BinaryDirectoryTreeModule(f));
          } else {
            scope.addClassFileToScope(loader, f);
          }
        }
      }
    } catch (IOException e) {
      Assertions.UNREACHABLE(e.toString());
    } catch (InvalidClassFileException e) {
      Assertions.UNREACHABLE(e.toString());
    }
  }
}
File
AnalysisScopeReader.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.util.io;// 5724-D15

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.zip.ZipException;

import com.ibm.wala.classLoader.JarFileModule;
import com.ibm.wala.classLoader.JarStreamModule;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.NestedJarFileModule;

/**
 * This class provides files that are packaged with this plug-in
 */
public class FileProvider {


  private final static int DEBUG_LEVEL = 0;
  
  /**
   * @param fileName
   * @return the jar file packaged with this plug-in of the given name, or null
   *         if not found.
   */
  public Module getJarFileModule(String fileName) throws IOException {
    return getJarFileModule(fileName, FileProvider.class.getClassLoader());
  }

  public Module getJarFileModule(String fileName, ClassLoader loader) throws IOException {
    return getJarFileFromClassLoader(fileName, loader);
  }

  public URL getResource(String fileName) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    return getResource(fileName, FileProvider.class.getClassLoader());
  }

  public URL getResource(String fileName, ClassLoader loader) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    return loader.getResource(fileName);
  }

  public File getFile(String fileName) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    return getFile(fileName, FileProvider.class.getClassLoader());
  }

  public File getFile(String fileName, ClassLoader loader) throws IOException {
    return getFileFromClassLoader(fileName, loader);
  }

  /**
   * @throws FileNotFoundException
   */
  public File getFileFromClassLoader(String fileName, ClassLoader loader) throws FileNotFoundException {
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    URL url = null;
    try {
      url = loader.getResource(fileName);
    } catch (Exception e) {
    }    
    if (DEBUG_LEVEL > 0) {
      System.err.println(("FileProvider got url: " + url + " for " + fileName));
    }
    if (url == null) {
      // couldn't load it from the class loader. try again from the
      // system classloader
      File f = new File(fileName);
      if (f.exists()) {
        return f;
      }
      throw new FileNotFoundException(fileName);
    } else {
      return new File(filePathFromURL(url));
    }
  }

  /**
   * @throws FileNotFoundException
   */
  public InputStream getInputStreamFromClassLoader(String fileName, ClassLoader loader) throws FileNotFoundException {
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    InputStream is = loader.getResourceAsStream(fileName);
    if (is == null) {
      throw new FileNotFoundException(fileName);
    }
    return is;
  }

  /**
   * @return the jar file packaged with this plug-in of the given name, or null
   *         if not found: wrapped as a JarFileModule or a NestedJarFileModule
   * @throws IOException
   */
  public Module getJarFileFromClassLoader(String fileName, ClassLoader loader) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    URL url = loader.getResource(fileName);
    if (DEBUG_LEVEL > 0) {
      System.err.println("FileProvider got url: " + url + " for " + fileName);
    }
    if (url == null) {
      // couldn't load it from the class loader. try again from the
      // system classloader
      try {
        return new JarFileModule(new JarFile(fileName, false));
      } catch (ZipException e) {
        throw new IOException("Could not find file: " + fileName);
      }
    }
    if (url.getProtocol().equals("jar")) {
      JarURLConnection jc = (JarURLConnection) url.openConnection();
      JarFile f = jc.getJarFile();
      JarEntry entry = jc.getJarEntry();
      JarFileModule parent = new JarFileModule(f);
      return new NestedJarFileModule(parent, entry);
/** BEGIN Custom change: try to load from input stream as fallback */
    } else if (url.getProtocol().equals("file")) {
      String filePath = filePathFromURL(url);
      return new JarFileModule(new JarFile(filePath, false));
    } else {
      final URLConnection in = url.openConnection();
      final JarInputStream jarIn = new JarInputStream(in.getInputStream());
      return new JarStreamModule(jarIn);
/** END Custom change: try to load from input stream as fallback */
    }
  }

  /**
   * Properly creates the String file name of a {@link URL}. This works around a
   * bug in the Sun implementation of {@link URL#getFile()}, which doesn't
   * properly handle file paths with spaces (see bug report). For now, fails with an assertion if the url is malformed.
   * 
   * @param url
   * @return the path name for the url
   * @throws IllegalArgumentException
   *           if url is null
   */
  public String filePathFromURL(URL url) {
    if (url == null) {
      throw new IllegalArgumentException("url is null");
    }
    // Old solution does not deal well with "<" | ">" | "#" | "%" |
    // <">  "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`" since they may occur
    // inside an URL but are prohibited for an URI. See
    // http://www.faqs.org/rfcs/rfc2396.html Section 2.4.3
    // This solution works. See discussion at
    // http://stackoverflow.com/questions/4494063/how-to-avoid-java-net-urisyntaxexception-in-url-touri
    URI uri = new File(url.getPath()).toURI();
    String filePath = uri.getPath();
    return filePath;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.util.io;// 5724-D15

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipException;

import com.ibm.wala.classLoader.JarFileModule;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.NestedJarFileModule;

/**
 * This class provides files that are packaged with this plug-in
 */
public class FileProvider {


  private final static int DEBUG_LEVEL = 0;
  
  /**
   * @param fileName
   * @return the jar file packaged with this plug-in of the given name, or null
   *         if not found.
   */
  public Module getJarFileModule(String fileName) throws IOException {
    return getJarFileModule(fileName, FileProvider.class.getClassLoader());
  }

  public Module getJarFileModule(String fileName, ClassLoader loader) throws IOException {
    return getJarFileFromClassLoader(fileName, loader);
  }

  public URL getResource(String fileName) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    return getResource(fileName, FileProvider.class.getClassLoader());
  }

  public URL getResource(String fileName, ClassLoader loader) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    return loader.getResource(fileName);
  }

  public File getFile(String fileName) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    return getFile(fileName, FileProvider.class.getClassLoader());
  }

  public File getFile(String fileName, ClassLoader loader) throws IOException {
    return getFileFromClassLoader(fileName, loader);
  }

  /**
   * @throws FileNotFoundException
   */
  public File getFileFromClassLoader(String fileName, ClassLoader loader) throws FileNotFoundException {
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    URL url = null;
    try {
      url = loader.getResource(fileName);
    } catch (Exception e) {
    }    
    if (DEBUG_LEVEL > 0) {
      System.err.println(("FileProvider got url: " + url + " for " + fileName));
    }
    if (url == null) {
      // couldn't load it from the class loader. try again from the
      // system classloader
      File f = new File(fileName);
      if (f.exists()) {
        return f;
      }
      throw new FileNotFoundException(fileName);
    } else {
      return new File(filePathFromURL(url));
    }
  }

  /**
   * @throws FileNotFoundException
   */
  public InputStream getInputStreamFromClassLoader(String fileName, ClassLoader loader) throws FileNotFoundException {
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    InputStream is = loader.getResourceAsStream(fileName);
    if (is == null) {
      throw new FileNotFoundException(fileName);
    }
    return is;
  }

  /**
   * @return the jar file packaged with this plug-in of the given name, or null
   *         if not found: wrapped as a JarFileModule or a NestedJarFileModule
   * @throws IOException
   */
  public Module getJarFileFromClassLoader(String fileName, ClassLoader loader) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    URL url = loader.getResource(fileName);
    if (DEBUG_LEVEL > 0) {
      System.err.println("FileProvider got url: " + url + " for " + fileName);
    }
    if (url == null) {
      // couldn't load it from the class loader. try again from the
      // system classloader
      try {
        return new JarFileModule(new JarFile(fileName, false));
      } catch (ZipException e) {
        throw new IOException("Could not find file: " + fileName);
      }
    }
    if (url.getProtocol().equals("jar")) {
      JarURLConnection jc = (JarURLConnection) url.openConnection();
      JarFile f = jc.getJarFile();
      JarEntry entry = jc.getJarEntry();
      JarFileModule parent = new JarFileModule(f);
      return new NestedJarFileModule(parent, entry);
    } else {
      String filePath = filePathFromURL(url);
      return new JarFileModule(new JarFile(filePath, false));
    }
  }

  /**
   * Properly creates the String file name of a {@link URL}. This works around a
   * bug in the Sun implementation of {@link URL#getFile()}, which doesn't
   * properly handle file paths with spaces (see bug report). For now, fails with an assertion if the url is malformed.
   * 
   * @param url
   * @return the path name for the url
   * @throws IllegalArgumentException
   *           if url is null
   */
  public String filePathFromURL(URL url) {
    if (url == null) {
      throw new IllegalArgumentException("url is null");
    }
    // Old solution does not deal well with "<" | ">" | "#" | "%" |
    // <">  "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`" since they may occur
    // inside an URL but are prohibited for an URI. See
    // http://www.faqs.org/rfcs/rfc2396.html Section 2.4.3
    // This solution works. See discussion at
    // http://stackoverflow.com/questions/4494063/how-to-avoid-java-net-urisyntaxexception-in-url-touri
    URI uri = new File(url.getPath()).toURI();
    String filePath = uri.getPath();
    return filePath;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.util.io;// 5724-D15

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.zip.ZipException;

import com.ibm.wala.classLoader.JarFileModule;
import com.ibm.wala.classLoader.JarStreamModule;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.NestedJarFileModule;

/**
 * This class provides files that are packaged with this plug-in
 */
public class FileProvider {


  private final static int DEBUG_LEVEL = 0;
  
  /**
   * @param fileName
   * @return the jar file packaged with this plug-in of the given name, or null
   *         if not found.
   */
  public Module getJarFileModule(String fileName) throws IOException {
    return getJarFileModule(fileName, FileProvider.class.getClassLoader());
  }

  public Module getJarFileModule(String fileName, ClassLoader loader) throws IOException {
    return getJarFileFromClassLoader(fileName, loader);
  }

  public URL getResource(String fileName) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    return getResource(fileName, FileProvider.class.getClassLoader());
  }

  public URL getResource(String fileName, ClassLoader loader) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    return loader.getResource(fileName);
  }

  public File getFile(String fileName) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    return getFile(fileName, FileProvider.class.getClassLoader());
  }

  public File getFile(String fileName, ClassLoader loader) throws IOException {
    return getFileFromClassLoader(fileName, loader);
  }

  /**
   * @throws FileNotFoundException
   */
  public File getFileFromClassLoader(String fileName, ClassLoader loader) throws FileNotFoundException {
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    URL url = null;
    try {
      url = loader.getResource(fileName);
    } catch (Exception e) {
    }    
    if (DEBUG_LEVEL > 0) {
      System.err.println(("FileProvider got url: " + url + " for " + fileName));
    }
    if (url == null) {
      // couldn't load it from the class loader. try again from the
      // system classloader
      File f = new File(fileName);
      if (f.exists()) {
        return f;
      }
      throw new FileNotFoundException(fileName);
    } else {
      return new File(filePathFromURL(url));
    }
  }

  /**
   * @throws FileNotFoundException
   */
  public InputStream getInputStreamFromClassLoader(String fileName, ClassLoader loader) throws FileNotFoundException {
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    InputStream is = loader.getResourceAsStream(fileName);
    if (is == null) {
      throw new FileNotFoundException(fileName);
    }
    return is;
  }

  /**
   * @return the jar file packaged with this plug-in of the given name, or null
   *         if not found: wrapped as a JarFileModule or a NestedJarFileModule
   * @throws IOException
   */
  public Module getJarFileFromClassLoader(String fileName, ClassLoader loader) throws IOException {
    if (fileName == null) {
      throw new IllegalArgumentException("null fileName");
    }
    if (loader == null) {
      throw new IllegalArgumentException("null loader");
    }
    URL url = loader.getResource(fileName);
    if (DEBUG_LEVEL > 0) {
      System.err.println("FileProvider got url: " + url + " for " + fileName);
    }
    if (url == null) {
      // couldn't load it from the class loader. try again from the
      // system classloader
      try {
        return new JarFileModule(new JarFile(fileName, false));
      } catch (ZipException e) {
        throw new IOException("Could not find file: " + fileName);
      }
    }
    if (url.getProtocol().equals("jar")) {
      JarURLConnection jc = (JarURLConnection) url.openConnection();
      JarFile f = jc.getJarFile();
      JarEntry entry = jc.getJarEntry();
      JarFileModule parent = new JarFileModule(f);
      return new NestedJarFileModule(parent, entry);
/** BEGIN Custom change: try to load from input stream as fallback */
    } else if (url.getProtocol().equals("file")) {
      String filePath = filePathFromURL(url);
      return new JarFileModule(new JarFile(filePath, false));
    } else {
      final URLConnection in = url.openConnection();
      final JarInputStream jarIn = new JarInputStream(in.getInputStream());
      return new JarStreamModule(jarIn);
/** END Custom change: try to load from input stream as fallback */
    }
  }

  /**
   * Properly creates the String file name of a {@link URL}. This works around a
   * bug in the Sun implementation of {@link URL#getFile()}, which doesn't
   * properly handle file paths with spaces (see bug report). For now, fails with an assertion if the url is malformed.
   * 
   * @param url
   * @return the path name for the url
   * @throws IllegalArgumentException
   *           if url is null
   */
  public String filePathFromURL(URL url) {
    if (url == null) {
      throw new IllegalArgumentException("url is null");
    }
    // Old solution does not deal well with "<" | ">" | "#" | "%" |
    // <">  "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`" since they may occur
    // inside an URL but are prohibited for an URI. See
    // http://www.faqs.org/rfcs/rfc2396.html Section 2.4.3
    // This solution works. See discussion at
    // http://stackoverflow.com/questions/4494063/how-to-avoid-java-net-urisyntaxexception-in-url-touri
    URI uri = new File(url.getPath()).toURI();
    String filePath = uri.getPath();
    return filePath;
  }

}
File
FileProvider.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
   */
   * @param d
   */
    if (MORE_VERBOSE) {

  /**
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.fixedpoint.impl;

import java.util.Iterator;
import java.util.LinkedList;

import com.ibm.wala.fixpoint.AbstractOperator;
import com.ibm.wala.fixpoint.AbstractStatement;
import com.ibm.wala.fixpoint.FixedPointConstants;
import com.ibm.wala.fixpoint.IFixedPointSolver;
import com.ibm.wala.fixpoint.IFixedPointStatement;
import com.ibm.wala.fixpoint.IVariable;
import com.ibm.wala.fixpoint.UnaryOperator;
import com.ibm.wala.fixpoint.UnaryStatement;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.MonitorUtil;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.debug.VerboseAction;

/**
 * Represents a set of {@link IFixedPointStatement}s to be solved by a {@link IFixedPointSolver}
 * 
 * 

* Implementation Note: * * The set of steps and variables is internally represented as a graph. Each step and each variable is a node in the graph. If a * step produces a variable that is used by another step, the graph has a directed edge from the producer to the consumer. * Fixed-point iteration proceeds in a topological order according to these edges. */ @SuppressWarnings("rawtypes") public abstract class AbstractFixedPointSolver> implements IFixedPointSolver, FixedPointConstants, VerboseAction { static final boolean DEBUG = false; static public final boolean verbose = "true".equals(System.getProperty("com.ibm.wala.fixedpoint.impl.verbose")); static public final int DEFAULT_VERBOSE_INTERVAL = 100000; static final boolean MORE_VERBOSE = true; static public final int DEFAULT_PERIODIC_MAINTENANCE_INTERVAL = 100000; /** * A tuning parameter; how may new IStatementDefinitionss must be added before doing a new topological sort? TODO: Tune this * empirically. */ private int minSizeForTopSort = 0; /** * A tuning parameter; by what percentage must the number of equations grow before we perform a topological sort? */ private double topologicalGrowthFactor = 0.1; * A tuning parameter: how many evaluations are allowed to take place between topological re-orderings. The idea is that many * evaluations may be a sign of a bad ordering, even when few new equations are being added. * * A number less than zero mean infinite. */ private int maxEvalBetweenTopo = 500000; private int evaluationsAtLastOrdering = 0; /** * How many equations have been added since the last topological sort? */ int topologicalCounter = 0; /** * The next order number to assign to a new equation */ int nextOrderNumber = 1; /** * During verbose evaluation, holds the number of dataflow equations evaluated */ private int nEvaluated = 0; /** * During verbose evaluation, holds the number of dataflow equations created */ private int nCreated = 0; /** * worklist for the iterative solver */ protected Worklist workList = new Worklist(); /** * A boolean which is initially true, but set to false after the first call to solve(); */ private boolean firstSolve = true; protected abstract T[] makeStmtRHS(int size); /** * Some setup which occurs only before the first solve */ public void initForFirstSolve() { orderStatements(); initializeVariables(); initializeWorkList(); firstSolve = false; } /** * @return true iff work list is empty */ public boolean emptyWorkList() { return workList.isEmpty(); } /** * Solve the set of dataflow graph. *

* PRECONDITION: graph is set up * public void addAllStatementsToWorkList() { * @return true iff the evaluation of some equation caused a change in the value of some variable. */ @SuppressWarnings("unchecked") public boolean solve(IProgressMonitor monitor) throws CancelException { boolean globalChange = false; if (firstSolve) { initForFirstSolve(); } while (!workList.isEmpty()) { MonitorUtil.throwExceptionIfCanceled(monitor); orderStatements(); // duplicate insertion detection AbstractStatement s = workList.takeStatement(); if (DEBUG) { System.err.println(("Before evaluation " + s)); } byte code = s.evaluate(); if (verbose) { nEvaluated++; if (nEvaluated % getVerboseInterval() == 0) { performVerboseAction(); } if (nEvaluated % getPeriodicMaintainInterval() == 0) { periodicMaintenance(); } } if (DEBUG) { System.err.println(("After evaluation " + s + " " + isChanged(code))); } if (isChanged(code)) { globalChange = true; updateWorkList(s); } if (isFixed(code)) { removeStatement(s); } } return globalChange; } public void performVerboseAction() { System.err.println("Evaluated " + nEvaluated); System.err.println("Created " + nCreated); System.err.println("Worklist " + workList.size()); if (MORE_VERBOSE) { if (!workList.isEmpty()) { AbstractStatement s = workList.takeStatement(); System.err.println("Peek " + lineBreak(s.toString(), 132)); if (s instanceof VerboseAction) { ((VerboseAction) s).performVerboseAction(); } workList.insertStatement(s); } } } public static String lineBreak(String string, int wrap) { if (string == null) { throw new IllegalArgumentException("string is null"); } if (string.length() > wrap) { StringBuffer result = new StringBuffer(); int start = 0; while (start < string.length()) { int end = Math.min(start + wrap, string.length()); result.append(string.substring(start, end)); result.append("\n "); start = end; } return result.toString(); } else { return string; } } public void removeStatement(AbstractStatement s) { getFixedPointSystem().removeStatement(s); } @Override public String toString() { StringBuffer result = new StringBuffer("Fixed Point Sytem:\n"); for (Iterator it = getStatements(); it.hasNext();) { result.append(it.next()).append("\n"); } return result.toString(); } public Iterator getStatements() { return getFixedPointSystem().getStatements(); } /** * Add a step to the work list. * * @param s the step to add */ public void addToWorkList(AbstractStatement s) { workList.insertStatement(s); } /** * Add all to the work list. for (Iterator i = getStatements(); i.hasNext();) { AbstractStatement eq = (AbstractStatement) i.next(); addToWorkList(eq); } } /** * Call this method when the contents of a variable changes. This routine adds all graph using this variable to the set of new * graph. * * @param v the variable that has changed */ public void changedVariable(T v) { for (Iterator it = getFixedPointSystem().getStatementsThatUse(v); it.hasNext();) { AbstractStatement s = (AbstractStatement) it.next(); addToWorkList(s); } } /** * Add a step with zero operands on the right-hand side. * * TODO: this is a little odd, in that this equation will never fire unless explicitly added to a work list. I think in most cases * we shouldn't be creating this nullary form. * * @param lhs the variable set by this equation * @param operator the step operator * @throws IllegalArgumentException if lhs is null */ public boolean newStatement(final T lhs, final NullaryOperator operator, final boolean toWorkList, final boolean eager) { if (lhs == null) { throw new IllegalArgumentException("lhs is null"); } // add to the list of graph lhs.setOrderNumber(nextOrderNumber++); final NullaryStatement s = new BasicNullaryStatement(lhs, operator); if (getFixedPointSystem().containsStatement(s)) { return false; } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } @SuppressWarnings("unchecked") private void incorporateNewStatement(boolean toWorkList, boolean eager, AbstractStatement s) { if (eager) { byte code = s.evaluate(); if (verbose) { nEvaluated++; if (nEvaluated % getVerboseInterval() == 0) { performVerboseAction(); } if (nEvaluated % getPeriodicMaintainInterval() == 0) { periodicMaintenance(); } } if (isChanged(code)) { updateWorkList(s); } if (isFixed(code)) { removeStatement(s); } } else if (toWorkList) { addToWorkList(s); } } /** * Add a step with one operand on the right-hand side. * * @param lhs the lattice variable set by this equation * @param operator the step's operator * @param rhs first operand on the rhs * @return true iff the system changes * @throws IllegalArgumentException if operator is null */ public boolean newStatement(T lhs, UnaryOperator operator, T rhs, boolean toWorkList, boolean eager) { if (operator == null) { throw new IllegalArgumentException("operator is null"); } // add to the list of graph UnaryStatement s = operator.makeEquation(lhs, rhs); if (getFixedPointSystem().containsStatement(s)) { return false; } if (lhs != null) { lhs.setOrderNumber(nextOrderNumber++); } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } protected class Statement extends GeneralStatement { public Statement(T lhs, AbstractOperator operator, T op1, T op2, T op3) { /** super(lhs, operator, op1, op2, op3); } public Statement(T lhs, AbstractOperator operator, T op1, T op2) { super(lhs, operator, op1, op2); } public Statement(T lhs, AbstractOperator operator, T[] rhs) { super(lhs, operator, rhs); } public Statement(T lhs, AbstractOperator operator) { super(lhs, operator); } @Override protected T[] makeRHS(int size) { return makeStmtRHS(size); } } /** * Add an equation with two operands on the right-hand side. * * @param lhs the lattice variable set by this equation * @param operator the equation operator * @param op1 first operand on the rhs * @param op2 second operand on the rhs */ public boolean newStatement(T lhs, AbstractOperator operator, T op1, T op2, boolean toWorkList, boolean eager) { // add to the list of graph GeneralStatement s = new Statement(lhs, operator, op1, op2); if (getFixedPointSystem().containsStatement(s)) { return false; } if (lhs != null) { lhs.setOrderNumber(nextOrderNumber++); } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } /** * Add a step with three operands on the right-hand side. * * @param lhs the lattice variable set by this equation * @param operator the equation operator * @param op1 first operand on the rhs * @param op2 second operand on the rhs * @param op3 third operand on the rhs * @throws IllegalArgumentException if lhs is null */ public boolean newStatement(T lhs, AbstractOperator operator, T op1, T op2, T op3, boolean toWorkList, boolean eager) { if (lhs == null) { throw new IllegalArgumentException("lhs is null"); } // add to the list of graph lhs.setOrderNumber(nextOrderNumber++); GeneralStatement s = new Statement(lhs, operator, op1, op2, op3); if (getFixedPointSystem().containsStatement(s)) { nextOrderNumber--; return false; } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } /** * Add a step to the system with an arbitrary number of operands on the right-hand side. * * @param lhs lattice variable set by this equation * @param operator the operator * @param rhs the operands on the rhs */ public boolean newStatement(T lhs, AbstractOperator operator, T[] rhs, boolean toWorkList, boolean eager) { // add to the list of graph if (lhs != null) lhs.setOrderNumber(nextOrderNumber++); GeneralStatement s = new Statement(lhs, operator, rhs); if (getFixedPointSystem().containsStatement(s)) { nextOrderNumber--; return false; } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } /** * Initialize all lattice vars in the system. */ abstract protected void initializeVariables(); /** * Initialize the work list for iteration.j */ abstract protected void initializeWorkList(); /** * Update the worklist, assuming that a particular equation has been re-evaluated * * @param s the equation that has been re-evaluated. */ private void updateWorkList(AbstractStatement s) { // find each equation which uses this lattice cell, and // add it to the work list T v = s.getLHS(); if (v == null) { return; } changedVariable(v); } /** * Number the graph in topological order. */ private void orderStatementsInternal() { if (verbose) { if (nEvaluated > 0) { System.err.println("Reorder " + nEvaluated + " " + nCreated); } } reorder(); if (verbose) { if (nEvaluated > 0) { System.err.println("Reorder finished " + nEvaluated + " " + nCreated); } } topologicalCounter = 0; evaluationsAtLastOrdering = nEvaluated; } /** * */ public void orderStatements() { if (nextOrderNumber > minSizeForTopSort) { if (((double) topologicalCounter / (double) nextOrderNumber) > topologicalGrowthFactor) { orderStatementsInternal(); return; } } if ((nEvaluated - evaluationsAtLastOrdering) > maxEvalBetweenTopo) { orderStatementsInternal(); return; } } /** * Re-order the step definitions. */ private void reorder() { // drain the worklist LinkedList temp = new LinkedList(); while (!workList.isEmpty()) { AbstractStatement eq = workList.takeStatement(); temp.add(eq); } workList = new Worklist(); // compute new ordering getFixedPointSystem().reorder(); // re-populate worklist for (Iterator it = temp.iterator(); it.hasNext();) { AbstractStatement s = it.next(); workList.insertStatement(s); } } public static boolean isChanged(byte code) { return (code & CHANGED_MASK) != 0; } public static boolean isSideEffect(byte code) { return (code & SIDE_EFFECT_MASK) != 0; } public static boolean isFixed(byte code) { return (code & FIXED_MASK) != 0; } public int getMinSizeForTopSort() { return minSizeForTopSort; } /** * @param i */ public void setMinEquationsForTopSort(int i) { minSizeForTopSort = i; } public int getMaxEvalBetweenTopo() { return maxEvalBetweenTopo; } public double getTopologicalGrowthFactor() { return topologicalGrowthFactor; } /** * @param i */ public void setMaxEvalBetweenTopo(int i) { maxEvalBetweenTopo = i; } public void setTopologicalGrowthFactor(double d) { topologicalGrowthFactor = d; } public int getNumberOfEvaluations() { return nEvaluated; } public void incNumberOfEvaluations() { nEvaluated++; } /** * a method that will be called every N evaluations. subclasses should override as desired. */ protected void periodicMaintenance() { } /** * subclasses should override as desired. */ protected int getVerboseInterval() { return DEFAULT_VERBOSE_INTERVAL; } /** * subclasses should override as desired. */ protected int getPeriodicMaintainInterval() { return DEFAULT_PERIODIC_MAINTENANCE_INTERVAL; } } ======= /******************************************************************************* * Copyright (c) 2002 - 2006 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.fixedpoint.impl; import java.util.Iterator; import java.util.LinkedList; import com.ibm.wala.fixpoint.AbstractOperator; import com.ibm.wala.fixpoint.AbstractStatement; import com.ibm.wala.fixpoint.FixedPointConstants; import com.ibm.wala.fixpoint.IFixedPointSolver; import com.ibm.wala.fixpoint.IFixedPointStatement; import com.ibm.wala.fixpoint.IVariable; import com.ibm.wala.fixpoint.UnaryOperator; import com.ibm.wala.fixpoint.UnaryStatement; import com.ibm.wala.util.CancelException; import com.ibm.wala.util.MonitorUtil; import com.ibm.wala.util.MonitorUtil.IProgressMonitor; import com.ibm.wala.util.debug.VerboseAction; /** * Represents a set of {@link IFixedPointStatement}s to be solved by a {@link IFixedPointSolver} * *

* Implementation Note: * * The set of steps and variables is internally represented as a graph. Each step and each variable is a node in the graph. If a * step produces a variable that is used by another step, the graph has a directed edge from the producer to the consumer. * Fixed-point iteration proceeds in a topological order according to these edges. */ @SuppressWarnings("rawtypes") public abstract class AbstractFixedPointSolver> implements IFixedPointSolver, FixedPointConstants, VerboseAction { static final boolean DEBUG = false; static public final boolean verbose = ("true".equals(System.getProperty("com.ibm.wala.fixedpoint.impl.verbose")) ? true : false); static public final int DEFAULT_VERBOSE_INTERVAL = 100000; static final boolean MORE_VERBOSE = true; static public final int DEFAULT_PERIODIC_MAINTENANCE_INTERVAL = 100000; /** * A tuning parameter; how may new IStatementDefinitionss must be added before doing a new topological sort? TODO: Tune this * empirically. */ private int minSizeForTopSort = 0; /** * A tuning parameter; by what percentage must the number of equations grow before we perform a topological sort? */ private double topologicalGrowthFactor = 0.1; /** * A tuning parameter: how many evaluations are allowed to take place between topological re-orderings. The idea is that many * evaluations may be a sign of a bad ordering, even when few new equations are being added. * * A number less than zero mean infinite. */ private int maxEvalBetweenTopo = 500000; private int evaluationsAtLastOrdering = 0; /** * How many equations have been added since the last topological sort? */ int topologicalCounter = 0; /** * The next order number to assign to a new equation */ int nextOrderNumber = 1; /** * During verbose evaluation, holds the number of dataflow equations evaluated */ private int nEvaluated = 0; /** * During verbose evaluation, holds the number of dataflow equations created */ private int nCreated = 0; /** * worklist for the iterative solver */ protected Worklist workList = new Worklist(); /** * A boolean which is initially true, but set to false after the first call to solve(); */ private boolean firstSolve = true; protected abstract T[] makeStmtRHS(int size); /** * Some setup which occurs only before the first solve */ public void initForFirstSolve() { orderStatements(); initializeVariables(); initializeWorkList(); firstSolve = false; } /** * @return true iff work list is empty */ public boolean emptyWorkList() { return workList.isEmpty(); } /** * Solve the set of dataflow graph. *

* PRECONDITION: graph is set up * * @return true iff the evaluation of some equation caused a change in the value of some variable. */ @SuppressWarnings("unchecked") public boolean solve(IProgressMonitor monitor) throws CancelException { boolean globalChange = false; if (firstSolve) { initForFirstSolve(); } while (!workList.isEmpty()) { MonitorUtil.throwExceptionIfCanceled(monitor); orderStatements(); // duplicate insertion detection AbstractStatement s = workList.takeStatement(); if (DEBUG) { System.err.println(("Before evaluation " + s)); } byte code = s.evaluate(); if (verbose) { nEvaluated++; if (nEvaluated % getVerboseInterval() == 0) { performVerboseAction(); } if (nEvaluated % getPeriodicMaintainInterval() == 0) { periodicMaintenance(); } } if (DEBUG) { System.err.println(("After evaluation " + s + " " + isChanged(code))); } if (isChanged(code)) { globalChange = true; updateWorkList(s); } if (isFixed(code)) { removeStatement(s); } } return globalChange; } public void performVerboseAction() { System.err.println("Evaluated " + nEvaluated); System.err.println("Created " + nCreated); System.err.println("Worklist " + workList.size()); if (!workList.isEmpty()) { AbstractStatement s = workList.takeStatement(); System.err.println("Peek " + lineBreak(s.toString(), 132)); if (s instanceof VerboseAction) { ((VerboseAction) s).performVerboseAction(); } workList.insertStatement(s); } } } public static String lineBreak(String string, int wrap) { if (string == null) { throw new IllegalArgumentException("string is null"); } if (string.length() > wrap) { StringBuffer result = new StringBuffer(); int start = 0; while (start < string.length()) { int end = Math.min(start + wrap, string.length()); result.append(string.substring(start, end)); result.append("\n "); start = end; } return result.toString(); } else { return string; } } public void removeStatement(AbstractStatement s) { getFixedPointSystem().removeStatement(s); } @Override public String toString() { StringBuffer result = new StringBuffer("Fixed Point Sytem:\n"); for (Iterator it = getStatements(); it.hasNext();) { result.append(it.next()).append("\n"); } return result.toString(); } public Iterator getStatements() { return getFixedPointSystem().getStatements(); } /** * Add a step to the work list. * * @param s the step to add */ public void addToWorkList(AbstractStatement s) { workList.insertStatement(s); } /** * Add all to the work list. */ public void addAllStatementsToWorkList() { for (Iterator i = getStatements(); i.hasNext();) { AbstractStatement eq = (AbstractStatement) i.next(); addToWorkList(eq); } } /** * Call this method when the contents of a variable changes. This routine adds all graph using this variable to the set of new * graph. * * @param v the variable that has changed */ public void changedVariable(T v) { for (Iterator it = getFixedPointSystem().getStatementsThatUse(v); it.hasNext();) { AbstractStatement s = (AbstractStatement) it.next(); addToWorkList(s); } } /** * Add a step with zero operands on the right-hand side. * * TODO: this is a little odd, in that this equation will never fire unless explicitly added to a work list. I think in most cases * we shouldn't be creating this nullary form. * * @param lhs the variable set by this equation * @param operator the step operator * @throws IllegalArgumentException if lhs is null */ public boolean newStatement(final T lhs, final NullaryOperator operator, final boolean toWorkList, final boolean eager) { if (lhs == null) { throw new IllegalArgumentException("lhs is null"); } // add to the list of graph lhs.setOrderNumber(nextOrderNumber++); final NullaryStatement s = new BasicNullaryStatement(lhs, operator); if (getFixedPointSystem().containsStatement(s)) { return false; } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } @SuppressWarnings("unchecked") private void incorporateNewStatement(boolean toWorkList, boolean eager, AbstractStatement s) { if (eager) { byte code = s.evaluate(); if (verbose) { nEvaluated++; if (nEvaluated % getVerboseInterval() == 0) { performVerboseAction(); } if (nEvaluated % getPeriodicMaintainInterval() == 0) { periodicMaintenance(); } } if (isChanged(code)) { updateWorkList(s); } if (isFixed(code)) { removeStatement(s); } } else if (toWorkList) { addToWorkList(s); } } /** * Add a step with one operand on the right-hand side. * * @param lhs the lattice variable set by this equation * @param operator the step's operator * @param rhs first operand on the rhs * @return true iff the system changes * @throws IllegalArgumentException if operator is null */ public boolean newStatement(T lhs, UnaryOperator operator, T rhs, boolean toWorkList, boolean eager) { if (operator == null) { throw new IllegalArgumentException("operator is null"); } // add to the list of graph UnaryStatement s = operator.makeEquation(lhs, rhs); if (getFixedPointSystem().containsStatement(s)) { return false; } if (lhs != null) { lhs.setOrderNumber(nextOrderNumber++); } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } protected class Statement extends GeneralStatement { public Statement(T lhs, AbstractOperator operator, T op1, T op2, T op3) { super(lhs, operator, op1, op2, op3); } public Statement(T lhs, AbstractOperator operator, T op1, T op2) { super(lhs, operator, op1, op2); } public Statement(T lhs, AbstractOperator operator, T[] rhs) { super(lhs, operator, rhs); } public Statement(T lhs, AbstractOperator operator) { super(lhs, operator); } @Override protected T[] makeRHS(int size) { return makeStmtRHS(size); } } /** * Add an equation with two operands on the right-hand side. * * @param lhs the lattice variable set by this equation * @param operator the equation operator * @param op1 first operand on the rhs * @param op2 second operand on the rhs */ public boolean newStatement(T lhs, AbstractOperator operator, T op1, T op2, boolean toWorkList, boolean eager) { // add to the list of graph GeneralStatement s = new Statement(lhs, operator, op1, op2); if (getFixedPointSystem().containsStatement(s)) { return false; } if (lhs != null) { lhs.setOrderNumber(nextOrderNumber++); } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } /** * Add a step with three operands on the right-hand side. * * @param lhs the lattice variable set by this equation * @param operator the equation operator * @param op1 first operand on the rhs * @param op2 second operand on the rhs * @param op3 third operand on the rhs * @throws IllegalArgumentException if lhs is null */ public boolean newStatement(T lhs, AbstractOperator operator, T op1, T op2, T op3, boolean toWorkList, boolean eager) { if (lhs == null) { throw new IllegalArgumentException("lhs is null"); } // add to the list of graph lhs.setOrderNumber(nextOrderNumber++); GeneralStatement s = new Statement(lhs, operator, op1, op2, op3); if (getFixedPointSystem().containsStatement(s)) { nextOrderNumber--; return false; } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } /** * Add a step to the system with an arbitrary number of operands on the right-hand side. * * @param lhs lattice variable set by this equation * @param operator the operator * @param rhs the operands on the rhs */ public boolean newStatement(T lhs, AbstractOperator operator, T[] rhs, boolean toWorkList, boolean eager) { // add to the list of graph if (lhs != null) lhs.setOrderNumber(nextOrderNumber++); GeneralStatement s = new Statement(lhs, operator, rhs); if (getFixedPointSystem().containsStatement(s)) { nextOrderNumber--; return false; } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } /** * Initialize all lattice vars in the system. */ abstract protected void initializeVariables(); /** * Initialize the work list for iteration.j */ abstract protected void initializeWorkList(); /** * Update the worklist, assuming that a particular equation has been re-evaluated * * @param s the equation that has been re-evaluated. */ private void updateWorkList(AbstractStatement s) { // find each equation which uses this lattice cell, and // add it to the work list T v = s.getLHS(); if (v == null) { return; } changedVariable(v); } /** * Number the graph in topological order. */ private void orderStatementsInternal() { if (verbose) { if (nEvaluated > 0) { System.err.println("Reorder " + nEvaluated + " " + nCreated); } } reorder(); if (verbose) { if (nEvaluated > 0) { System.err.println("Reorder finished " + nEvaluated + " " + nCreated); } } topologicalCounter = 0; evaluationsAtLastOrdering = nEvaluated; } /** * */ public void orderStatements() { if (nextOrderNumber > minSizeForTopSort) { if (((double) topologicalCounter / (double) nextOrderNumber) > topologicalGrowthFactor) { orderStatementsInternal(); return; } } if ((nEvaluated - evaluationsAtLastOrdering) > maxEvalBetweenTopo) { orderStatementsInternal(); return; } } /** * Re-order the step definitions. */ private void reorder() { // drain the worklist LinkedList temp = new LinkedList(); while (!workList.isEmpty()) { AbstractStatement eq = workList.takeStatement(); temp.add(eq); } workList = new Worklist(); // compute new ordering getFixedPointSystem().reorder(); // re-populate worklist for (Iterator it = temp.iterator(); it.hasNext();) { AbstractStatement s = it.next(); workList.insertStatement(s); } } public static boolean isChanged(byte code) { return (code & CHANGED_MASK) != 0; } public static boolean isSideEffect(byte code) { return (code & SIDE_EFFECT_MASK) != 0; } public static boolean isFixed(byte code) { return (code & FIXED_MASK) != 0; } public int getMinSizeForTopSort() { return minSizeForTopSort; } /** * @param i */ public void setMinEquationsForTopSort(int i) { minSizeForTopSort = i; } public int getMaxEvalBetweenTopo() { return maxEvalBetweenTopo; } public double getTopologicalGrowthFactor() { return topologicalGrowthFactor; } /** * @param i */ public void setMaxEvalBetweenTopo(int i) { maxEvalBetweenTopo = i; } /** * @param d */ public void setTopologicalGrowthFactor(double d) { topologicalGrowthFactor = d; } public int getNumberOfEvaluations() { return nEvaluated; } public void incNumberOfEvaluations() { nEvaluated++; } /** * a method that will be called every N evaluations. subclasses should override as desired. */ protected void periodicMaintenance() { } /** * subclasses should override as desired. */ protected int getVerboseInterval() { return DEFAULT_VERBOSE_INTERVAL; } /** * subclasses should override as desired. */ protected int getPeriodicMaintainInterval() { return DEFAULT_PERIODIC_MAINTENANCE_INTERVAL; } } >>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6

Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.fixedpoint.impl;

import java.util.Iterator;
import java.util.LinkedList;

import com.ibm.wala.fixpoint.AbstractOperator;
import com.ibm.wala.fixpoint.AbstractStatement;
import com.ibm.wala.fixpoint.FixedPointConstants;
import com.ibm.wala.fixpoint.IFixedPointSolver;
import com.ibm.wala.fixpoint.IFixedPointStatement;
import com.ibm.wala.fixpoint.IVariable;
import com.ibm.wala.fixpoint.UnaryOperator;
import com.ibm.wala.fixpoint.UnaryStatement;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.MonitorUtil;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.debug.VerboseAction;

/**
 * Represents a set of {@link IFixedPointStatement}s to be solved by a {@link IFixedPointSolver}
 * 
 * 

* Implementation Note: * * The set of steps and variables is internally represented as a graph. Each step and each variable is a node in the graph. If a * step produces a variable that is used by another step, the graph has a directed edge from the producer to the consumer. * Fixed-point iteration proceeds in a topological order according to these edges. */ @SuppressWarnings("rawtypes") public abstract class AbstractFixedPointSolver> implements IFixedPointSolver, FixedPointConstants, VerboseAction { static final boolean DEBUG = false; static public final boolean verbose = "true".equals(System.getProperty("com.ibm.wala.fixedpoint.impl.verbose")); static public final int DEFAULT_VERBOSE_INTERVAL = 100000; static final boolean MORE_VERBOSE = true; static public final int DEFAULT_PERIODIC_MAINTENANCE_INTERVAL = 100000; /** * A tuning parameter; how may new IStatementDefinitionss must be added before doing a new topological sort? TODO: Tune this * empirically. */ private int minSizeForTopSort = 0; /** * A tuning parameter; by what percentage must the number of equations grow before we perform a topological sort? */ private double topologicalGrowthFactor = 0.1; /** * A tuning parameter: how many evaluations are allowed to take place between topological re-orderings. The idea is that many * evaluations may be a sign of a bad ordering, even when few new equations are being added. * * A number less than zero mean infinite. */ private int maxEvalBetweenTopo = 500000; private int evaluationsAtLastOrdering = 0; /** * How many equations have been added since the last topological sort? */ int topologicalCounter = 0; /** * The next order number to assign to a new equation */ int nextOrderNumber = 1; /** * During verbose evaluation, holds the number of dataflow equations evaluated */ private int nEvaluated = 0; /** * During verbose evaluation, holds the number of dataflow equations created */ private int nCreated = 0; /** * worklist for the iterative solver */ protected Worklist workList = new Worklist(); /** * A boolean which is initially true, but set to false after the first call to solve(); */ private boolean firstSolve = true; protected abstract T[] makeStmtRHS(int size); /** * Some setup which occurs only before the first solve */ public void initForFirstSolve() { orderStatements(); initializeVariables(); initializeWorkList(); firstSolve = false; } /** * @return true iff work list is empty */ public boolean emptyWorkList() { return workList.isEmpty(); } /** * Solve the set of dataflow graph. *

* PRECONDITION: graph is set up * * @return true iff the evaluation of some equation caused a change in the value of some variable. */ @SuppressWarnings("unchecked") public boolean solve(IProgressMonitor monitor) throws CancelException { boolean globalChange = false; if (firstSolve) { initForFirstSolve(); } while (!workList.isEmpty()) { MonitorUtil.throwExceptionIfCanceled(monitor); orderStatements(); // duplicate insertion detection AbstractStatement s = workList.takeStatement(); if (DEBUG) { } System.err.println(("Before evaluation " + s)); } byte code = s.evaluate(); if (verbose) { nEvaluated++; if (nEvaluated % getVerboseInterval() == 0) { performVerboseAction(); } if (nEvaluated % getPeriodicMaintainInterval() == 0) { periodicMaintenance(); } } if (DEBUG) { System.err.println(("After evaluation " + s + " " + isChanged(code))); } if (isChanged(code)) { globalChange = true; updateWorkList(s); } if (isFixed(code)) { removeStatement(s); } } return globalChange; } public void performVerboseAction() { System.err.println("Evaluated " + nEvaluated); System.err.println("Created " + nCreated); System.err.println("Worklist " + workList.size()); if (MORE_VERBOSE) { if (!workList.isEmpty()) { AbstractStatement s = workList.takeStatement(); System.err.println("Peek " + lineBreak(s.toString(), 132)); if (s instanceof VerboseAction) { ((VerboseAction) s).performVerboseAction(); } workList.insertStatement(s); } } } public static String lineBreak(String string, int wrap) { if (string == null) { throw new IllegalArgumentException("string is null"); } if (string.length() > wrap) { StringBuffer result = new StringBuffer(); int start = 0; while (start < string.length()) { int end = Math.min(start + wrap, string.length()); result.append(string.substring(start, end)); result.append("\n "); start = end; } return result.toString(); } else { return string; } } public void removeStatement(AbstractStatement s) { getFixedPointSystem().removeStatement(s); } @Override public String toString() { StringBuffer result = new StringBuffer("Fixed Point Sytem:\n"); for (Iterator it = getStatements(); it.hasNext();) { result.append(it.next()).append("\n"); } return result.toString(); } public Iterator getStatements() { return getFixedPointSystem().getStatements(); } /** * Add a step to the work list. * * @param s the step to add */ public void addToWorkList(AbstractStatement s) { workList.insertStatement(s); } /** * Add all to the work list. */ public void addAllStatementsToWorkList() { for (Iterator i = getStatements(); i.hasNext();) { AbstractStatement eq = (AbstractStatement) i.next(); addToWorkList(eq); } } /** * Call this method when the contents of a variable changes. This routine adds all graph using this variable to the set of new * graph. * * @param v the variable that has changed */ public void changedVariable(T v) { for (Iterator it = getFixedPointSystem().getStatementsThatUse(v); it.hasNext();) { AbstractStatement s = (AbstractStatement) it.next(); addToWorkList(s); } /** * Add a step with zero operands on the right-hand side. * * TODO: this is a little odd, in that this equation will never fire unless explicitly added to a work list. I think in most cases * we shouldn't be creating this nullary form. * * @param lhs the variable set by this equation * @param operator the step operator * @throws IllegalArgumentException if lhs is null */ public boolean newStatement(final T lhs, final NullaryOperator operator, final boolean toWorkList, final boolean eager) { if (lhs == null) { throw new IllegalArgumentException("lhs is null"); } // add to the list of graph lhs.setOrderNumber(nextOrderNumber++); final NullaryStatement s = new BasicNullaryStatement(lhs, operator); if (getFixedPointSystem().containsStatement(s)) { return false; } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } @SuppressWarnings("unchecked") private void incorporateNewStatement(boolean toWorkList, boolean eager, AbstractStatement s) { if (eager) { byte code = s.evaluate(); if (verbose) { nEvaluated++; if (nEvaluated % getVerboseInterval() == 0) { performVerboseAction(); } if (nEvaluated % getPeriodicMaintainInterval() == 0) { periodicMaintenance(); } } if (isChanged(code)) { updateWorkList(s); } if (isFixed(code)) { removeStatement(s); } } else if (toWorkList) { addToWorkList(s); } } /** * Add a step with one operand on the right-hand side. * * @param lhs the lattice variable set by this equation * @param operator the step's operator * @param rhs first operand on the rhs * @return true iff the system changes * @throws IllegalArgumentException if operator is null */ public boolean newStatement(T lhs, UnaryOperator operator, T rhs, boolean toWorkList, boolean eager) { if (operator == null) { throw new IllegalArgumentException("operator is null"); } // add to the list of graph UnaryStatement s = operator.makeEquation(lhs, rhs); if (getFixedPointSystem().containsStatement(s)) { return false; } if (lhs != null) { lhs.setOrderNumber(nextOrderNumber++); } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } protected class Statement extends GeneralStatement { public Statement(T lhs, AbstractOperator operator, T op1, T op2, T op3) { super(lhs, operator, op1, op2, op3); } public Statement(T lhs, AbstractOperator operator, T op1, T op2) { super(lhs, operator, op1, op2); } public Statement(T lhs, AbstractOperator operator, T[] rhs) { super(lhs, operator, rhs); } public Statement(T lhs, AbstractOperator operator) { super(lhs, operator); } @Override protected T[] makeRHS(int size) { return makeStmtRHS(size); } } /** * Add an equation with two operands on the right-hand side. * * @param lhs the lattice variable set by this equation * @param operator the equation operator * @param op1 first operand on the rhs * @param op2 second operand on the rhs */ public boolean newStatement(T lhs, AbstractOperator operator, T op1, T op2, boolean toWorkList, boolean eager) { // add to the list of graph GeneralStatement s = new Statement(lhs, operator, op1, op2); if (getFixedPointSystem().containsStatement(s)) { return false; } if (lhs != null) { lhs.setOrderNumber(nextOrderNumber++); } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } /** * Add a step with three operands on the right-hand side. * * @param lhs the lattice variable set by this equation * @param operator the equation operator * @param op1 first operand on the rhs * @param op2 second operand on the rhs * @param op3 third operand on the rhs * @throws IllegalArgumentException if lhs is null */ public boolean newStatement(T lhs, AbstractOperator operator, T op1, T op2, T op3, boolean toWorkList, boolean eager) { if (lhs == null) { throw new IllegalArgumentException("lhs is null"); } // add to the list of graph lhs.setOrderNumber(nextOrderNumber++); GeneralStatement s = new Statement(lhs, operator, op1, op2, op3); if (getFixedPointSystem().containsStatement(s)) { nextOrderNumber--; return false; } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } /** * Add a step to the system with an arbitrary number of operands on the right-hand side. * * @param lhs lattice variable set by this equation * @param operator the operator * @param rhs the operands on the rhs */ public boolean newStatement(T lhs, AbstractOperator operator, T[] rhs, boolean toWorkList, boolean eager) { // add to the list of graph if (lhs != null) lhs.setOrderNumber(nextOrderNumber++); GeneralStatement s = new Statement(lhs, operator, rhs); if (getFixedPointSystem().containsStatement(s)) { nextOrderNumber--; return false; } nCreated++; getFixedPointSystem().addStatement(s); incorporateNewStatement(toWorkList, eager, s); topologicalCounter++; return true; } /** * Initialize all lattice vars in the system. */ abstract protected void initializeVariables(); /** * Initialize the work list for iteration.j */ abstract protected void initializeWorkList(); /** * Update the worklist, assuming that a particular equation has been re-evaluated * * @param s the equation that has been re-evaluated. */ private void updateWorkList(AbstractStatement s) { // find each equation which uses this lattice cell, and // add it to the work list T v = s.getLHS(); if (v == null) { return; } changedVariable(v); } /** * Number the graph in topological order. */ private void orderStatementsInternal() { if (verbose) { if (nEvaluated > 0) { System.err.println("Reorder " + nEvaluated + " " + nCreated); } } reorder(); if (verbose) { if (nEvaluated > 0) { System.err.println("Reorder finished " + nEvaluated + " " + nCreated); } } topologicalCounter = 0; evaluationsAtLastOrdering = nEvaluated; } /** * */ public void orderStatements() { if (nextOrderNumber > minSizeForTopSort) { if (((double) topologicalCounter / (double) nextOrderNumber) > topologicalGrowthFactor) { orderStatementsInternal(); return; } } if ((nEvaluated - evaluationsAtLastOrdering) > maxEvalBetweenTopo) { orderStatementsInternal(); return; } } /** * Re-order the step definitions. */ private void reorder() { // drain the worklist LinkedList temp = new LinkedList(); while (!workList.isEmpty()) { AbstractStatement eq = workList.takeStatement(); temp.add(eq); } workList = new Worklist(); // compute new ordering getFixedPointSystem().reorder(); // re-populate worklist for (Iterator it = temp.iterator(); it.hasNext();) { AbstractStatement s = it.next(); workList.insertStatement(s); } } public static boolean isChanged(byte code) { return (code & CHANGED_MASK) != 0; } public static boolean isSideEffect(byte code) { return (code & SIDE_EFFECT_MASK) != 0; } public static boolean isFixed(byte code) { return (code & FIXED_MASK) != 0; } public int getMinSizeForTopSort() { return minSizeForTopSort; } /** * @param i */ public void setMinEquationsForTopSort(int i) { minSizeForTopSort = i; } public int getMaxEvalBetweenTopo() { return maxEvalBetweenTopo; } public double getTopologicalGrowthFactor() { return topologicalGrowthFactor; } /** * @param i */ public void setMaxEvalBetweenTopo(int i) { maxEvalBetweenTopo = i; } /** * @param d */ public void setTopologicalGrowthFactor(double d) { topologicalGrowthFactor = d; } public int getNumberOfEvaluations() { return nEvaluated; } public void incNumberOfEvaluations() { nEvaluated++; } /** * a method that will be called every N evaluations. subclasses should override as desired. */ protected void periodicMaintenance() { } /** * subclasses should override as desired. */ protected int getVerboseInterval() { return DEFAULT_VERBOSE_INTERVAL; } /** * subclasses should override as desired. */ protected int getPeriodicMaintainInterval() { return DEFAULT_PERIODIC_MAINTENANCE_INTERVAL; } }

File
AbstractFixedPointSolver.java
Developer's decision
Version 1
Kind of conflict
Annotation
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2007 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.util;



/**
 * Simple utilities for Eclipse progress monitors
 */
public class MonitorUtil {
  
  /**
   * Use this interface to decouple core utilities from the Eclipse layer
   */
  public interface IProgressMonitor {

    void beginTask(String task, int totalWork);

/** BEGIN Custom change: subtasks and canceling */
    void subTask(String subTask);

    void cancel();

/** END Custom change: subtasks and canceling */
    boolean isCanceled();

    void done();

    void worked(int units);
  }

  public static void beginTask(IProgressMonitor monitor, String task, int totalWork) throws CancelException {
    if (monitor != null) {
      monitor.beginTask(task, totalWork);
      if (monitor.isCanceled()) {
        throw CancelException.make("cancelled in " + task);
      }
    }
  }

  public static void done(IProgressMonitor monitor) throws CancelException {
    if (monitor != null) {
      monitor.done();
      if (monitor.isCanceled()) {
        throw CancelException.make("cancelled in " + monitor.toString());
      }
    }
  }

  public static void worked(IProgressMonitor monitor, int units) throws CancelException {
    if (monitor != null) {
      monitor.worked(units);
      if (monitor.isCanceled()) {
        throw CancelException.make("cancelled in " + monitor.toString());
      }
    }
  }

  public static void throwExceptionIfCanceled(IProgressMonitor progressMonitor) throws CancelException {
    if (progressMonitor != null) {
      if (progressMonitor.isCanceled()) {
        throw CancelException.make("operation cancelled");
      }
    }
  }
/** BEGIN Custom change: more on subtasks */

  public static void subTask(IProgressMonitor progressMonitor, String subTask) throws CancelException {
    if (progressMonitor != null) {
      progressMonitor.subTask(subTask);
      if (progressMonitor.isCanceled()) {
        throw CancelException.make("cancelled in " + subTask);
      }
    }
  }

  public static boolean isCanceled(IProgressMonitor progressMonitor) {
    if (progressMonitor == null) {
      return false;
    } else {
      return progressMonitor.isCanceled();
    }
  }
  
  public static void cancel(IProgressMonitor progress) {
    if (progress != null) {
      progress.cancel();
    }
  }
/** END Custom change: more on subtasks */

//  public static IProgressMonitor subProgress(ProgressMaster progress, int i) {
//    if (progress == null) {
//      return null;
//    } else {
//      return new SubProgressMonitor(progress, i);
//    }
//  }
}
=======
/*******************************************************************************
 * Copyright (c) 2007 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.util;



/**
 * Simple utilities for Eclipse progress monitors
 */
public class MonitorUtil {
  
  /**
   * Use this interface to decouple core utilities from the Eclipse layer
   */
  public interface IProgressMonitor {

    void beginTask(String task, int totalWork);

    boolean isCanceled();

    void done();

    void worked(int units);
  }

  public static void beginTask(IProgressMonitor monitor, String task, int totalWork) throws CancelException {
    if (monitor != null) {
      monitor.beginTask(task, totalWork);
      if (monitor.isCanceled()) {
        throw CancelException.make("cancelled in " + task);
      }
    }
  }

  public static void done(IProgressMonitor monitor) throws CancelException {
    if (monitor != null) {
      monitor.done();
      if (monitor.isCanceled()) {
        throw CancelException.make("cancelled in " + monitor.toString());
      }
    }
  }

  public static void worked(IProgressMonitor monitor, int units) throws CancelException {
    if (monitor != null) {
      monitor.worked(units);
      if (monitor.isCanceled()) {
        throw CancelException.make("cancelled in " + monitor.toString());
      }
    }
  }

  public static void throwExceptionIfCanceled(IProgressMonitor progressMonitor) throws CancelException {
    if (progressMonitor != null) {
      if (progressMonitor.isCanceled()) {
        throw CancelException.make("operation cancelled");
      }
    }
  }

//  public static IProgressMonitor subProgress(ProgressMaster progress, int i) {
//    if (progress == null) {
//      return null;
//    } else {
//      return new SubProgressMonitor(progress, i);
//    }
//  }
}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2007 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.util;



/**
 * Simple utilities for Eclipse progress monitors
 */
public class MonitorUtil {
  
  /**
   * Use this interface to decouple core utilities from the Eclipse layer
   */
  public interface IProgressMonitor {

    void beginTask(String task, int totalWork);

/** BEGIN Custom change: subtasks and canceling */
    void subTask(String subTask);

    void cancel();

/** END Custom change: subtasks and canceling */
    boolean isCanceled();

    void done();

    void worked(int units);
  }

  public static void beginTask(IProgressMonitor monitor, String task, int totalWork) throws CancelException {
    if (monitor != null) {
      monitor.beginTask(task, totalWork);
      if (monitor.isCanceled()) {
        throw CancelException.make("cancelled in " + task);
      }
    }
  }

  public static void done(IProgressMonitor monitor) throws CancelException {
    if (monitor != null) {
      monitor.done();
      if (monitor.isCanceled()) {
        throw CancelException.make("cancelled in " + monitor.toString());
      }
    }
  }

  public static void worked(IProgressMonitor monitor, int units) throws CancelException {
    if (monitor != null) {
      monitor.worked(units);
      if (monitor.isCanceled()) {
        throw CancelException.make("cancelled in " + monitor.toString());
      }
    }
  }

  public static void throwExceptionIfCanceled(IProgressMonitor progressMonitor) throws CancelException {
    if (progressMonitor != null) {
      if (progressMonitor.isCanceled()) {
        throw CancelException.make("operation cancelled");
      }
    }
  }
/** BEGIN Custom change: more on subtasks */

  public static void subTask(IProgressMonitor progressMonitor, String subTask) throws CancelException {
    if (progressMonitor != null) {
      progressMonitor.subTask(subTask);
      if (progressMonitor.isCanceled()) {
        throw CancelException.make("cancelled in " + subTask);
      }
    }
  }

  public static boolean isCanceled(IProgressMonitor progressMonitor) {
    if (progressMonitor == null) {
      return false;
    } else {
      return progressMonitor.isCanceled();
    }
  }
  
  public static void cancel(IProgressMonitor progress) {
    if (progress != null) {
      progress.cancel();
    }
  }
/** END Custom change: more on subtasks */

//  public static IProgressMonitor subProgress(ProgressMaster progress, int i) {
//    if (progress == null) {
//      return null;
//    } else {
//      return new SubProgressMonitor(progress, i);
//    }
//  }
}
File
MonitorUtil.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Package declaration
Chunk
Conflicting content
   */
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.util.intset;

import java.util.Collection;
import java.util.Iterator;

import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.debug.Assertions;

/**
 * A Set backed by a set of integers.
 */
public class OrdinalSet implements Iterable {

  private final IntSet S;

  private final OrdinalSetMapping mapping;

  @SuppressWarnings("rawtypes")
  private final static OrdinalSet EMPTY = new OrdinalSet();

  @SuppressWarnings("unchecked")
  public static  OrdinalSet empty() {
    return EMPTY;
  }

  private OrdinalSet() {
    S = null;
    mapping = null;
  }

  }
  public OrdinalSet(IntSet S, OrdinalSetMapping mapping) {
    this.S = S;
    this.mapping = mapping;
  }

  public boolean containsAny(OrdinalSet that) {
    if (that == null) {
      throw new IllegalArgumentException("null that");
    }
    if (S == null || that.S == null) {
      return false;
    }
    return S.containsAny(that.S);
  }

  public int size() {
    return (S == null) ? 0 : S.size();
  }

  public Iterator iterator() {
    if (S == null) {
      return EmptyIterator.instance();
    } else {

      return new Iterator() {
        IntIterator it = S.intIterator();

        public boolean hasNext() {
          return it.hasNext();
        }

        public T next() {
          return mapping.getMappedObject(it.next());
        }

        public void remove() {
          Assertions.UNREACHABLE();
        }
      };
    }
  }

  /**
   * @return a new OrdinalSet instances
   * @throws IllegalArgumentException if A is null
  public static  OrdinalSet intersect(OrdinalSet A, OrdinalSet B) {
    if (A == null) {
      throw new IllegalArgumentException("A is null");
    }
    if (A.size() != 0 && B.size() != 0) {
      assert A.mapping.equals(B.mapping);
    }
    if (A.S == null || B.S == null) {
      return new OrdinalSet(null, A.mapping);
    }
    IntSet isect = A.S.intersection(B.S);
    return new OrdinalSet(isect, A.mapping);
  }

/** BEGIN Custom change: equals for OrdinalSets */
  /**
   * @return true if the contents of two sets are equal
   */
  public static  boolean equals(OrdinalSet a, OrdinalSet b) {
    if (a == null && b == null) {
      return true;
    }
    
    if (a != null && b != null && a.size() == b.size()) {
      if (a.mapping == b.mapping || (a.mapping != null && b.mapping != null && a.mapping.equals(b.mapping))) {
        return a.S == b.S || (a.S != null && b.S != null && a.S.sameValue(b.S));
      }
    }
    
    return false;
  }
  
/** END Custom change: equals for OrdinalSets */
  /**
   * Creates the union of two ordinal sets.
   * 
   * @param A ordinal set a
   * @param B ordinal set b
   * @return union of a and b
   * @throws IllegalArgumentException iff A or B is null
   */
  public static  OrdinalSet unify(OrdinalSet A, OrdinalSet B) {
    if (A == null) {
      throw new IllegalArgumentException("A is null");
    }
    if (B == null) {
      throw new IllegalArgumentException("B is null");
    }
    if (A.size() != 0 && B.size() != 0) {
      assert A.mapping.equals(B.mapping);
    }

    if (A.S == null) {
      return (B.S == null) ? OrdinalSet. empty() : new OrdinalSet(B.S, B.mapping);
    } else if (B.S == null) {
      return new OrdinalSet(A.S, A.mapping);
    }

    IntSet union = A.S.union(B.S);
    return new OrdinalSet(union, A.mapping);
  }

  @Override
  public String toString() {
    return Iterator2Collection.toSet(iterator()).toString();

  /**
   */
  public SparseIntSet makeSparseCopy() {
    return (S == null) ? new SparseIntSet() : new SparseIntSet(S);
  }

  /**
   * Dangerous. Added for performance reasons. Use this only if you really know what you are doing.
   */
  public IntSet getBackingSet() {
    return S;
  }

  /**
   * @return true iff this set contains object
   */
  public boolean contains(T object) {
    if (this == EMPTY || S == null || object == null) {
      return false;
    }
    int index = mapping.getMappedIndex(object);
    return (index == -1) ? false : S.contains(index);
  }

  public boolean isEmpty() {
    return size() == 0;
  }

  /**
   * @throws NullPointerException if instances is null
   */
  public static  Collection toCollection(OrdinalSet instances) {
    return Iterator2Collection.toSet(instances.iterator());
  }

  /**
   * Precondition: the ordinal set mapping has an index for every element of c Convert a "normal" collection to an OrdinalSet, based
   * on the given mapping.
   * 
   * @throws IllegalArgumentException if c is null
   */
  public static  OrdinalSet toOrdinalSet(Collection c, OrdinalSetMapping m) {
    if (c == null) {
      throw new IllegalArgumentException("c is null");
    }
    if (m == null) {
      throw new IllegalArgumentException("m is null");
    }
    MutableSparseIntSet s = MutableSparseIntSet.makeEmpty();
    for (Iterator it = c.iterator(); it.hasNext();) {
      int index = m.getMappedIndex(it.next());
      assert index >= 0;
      s.add(index);
    }
    return new OrdinalSet(s, m);
  }

  public OrdinalSetMapping getMapping() {
    return mapping;
  }

=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.util.intset;

import java.util.Collection;
import java.util.Iterator;

import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.debug.Assertions;

/**
 * A Set backed by a set of integers.
 */
public class OrdinalSet implements Iterable {

  private final IntSet S;

  private final OrdinalSetMapping mapping;

  @SuppressWarnings("rawtypes")
  private final static OrdinalSet EMPTY = new OrdinalSet();

  @SuppressWarnings("unchecked")
  public static  OrdinalSet empty() {
    return EMPTY;
  }

  private OrdinalSet() {
    S = null;
    mapping = null;
  }

  public OrdinalSet(IntSet S, OrdinalSetMapping mapping) {
    this.S = S;
    this.mapping = mapping;
  }

  public boolean containsAny(OrdinalSet that) {
    if (that == null) {
      throw new IllegalArgumentException("null that");
    }
    if (S == null || that.S == null) {
      return false;
    }
    return S.containsAny(that.S);
  }

  public int size() {
    return (S == null) ? 0 : S.size();
  }

  public Iterator iterator() {
    if (S == null) {
      return EmptyIterator.instance();
    } else {

      return new Iterator() {
        IntIterator it = S.intIterator();

        public boolean hasNext() {
          return it.hasNext();
        }

        public T next() {
          return mapping.getMappedObject(it.next());
        }

        public void remove() {
          Assertions.UNREACHABLE();
        }
      };
    }
  }

  /**
   * @return a new OrdinalSet instances
   * @throws IllegalArgumentException if A is null
   */
  public static  OrdinalSet intersect(OrdinalSet A, OrdinalSet B) {
    if (A == null) {
      throw new IllegalArgumentException("A is null");
    }
    if (A.size() != 0 && B.size() != 0) {
      assert A.mapping.equals(B.mapping);
    }
    if (A.S == null || B.S == null) {
      return new OrdinalSet(null, A.mapping);
    }
    IntSet isect = A.S.intersection(B.S);
    return new OrdinalSet(isect, A.mapping);
  }

  /**
   * Creates the union of two ordinal sets.
   * 
   * @param A ordinal set a
   * @param B ordinal set b
   * @return union of a and b
   * @throws IllegalArgumentException iff A or B is null
   */
  public static  OrdinalSet unify(OrdinalSet A, OrdinalSet B) {
    if (A == null) {
      throw new IllegalArgumentException("A is null");
    }
    if (B == null) {
      throw new IllegalArgumentException("B is null");
    }
    if (A.size() != 0 && B.size() != 0) {
      assert A.mapping.equals(B.mapping);
    }

    if (A.S == null) {
      return (B.S == null) ? OrdinalSet. empty() : new OrdinalSet(B.S, B.mapping);
    } else if (B.S == null) {
      return new OrdinalSet(A.S, A.mapping);
    }

    IntSet union = A.S.union(B.S);
    return new OrdinalSet(union, A.mapping);
  }

  @Override
  public String toString() {
    return Iterator2Collection.toSet(iterator()).toString();
  }

  /**
   */
  public SparseIntSet makeSparseCopy() {
    return (S == null) ? new SparseIntSet() : new SparseIntSet(S);
  }

  /**
   * Dangerous. Added for performance reasons. Use this only if you really know what you are doing.
   */
  public IntSet getBackingSet() {
    return S;
  }

  /**
   * @return true iff this set contains object
   */
  public boolean contains(T object) {
    if (this == EMPTY || S == null || object == null) {
      return false;
    }
    int index = mapping.getMappedIndex(object);
    return (index == -1) ? false : S.contains(index);
  }

  public boolean isEmpty() {
    return size() == 0;
  }

  /**
   * @throws NullPointerException if instances is null
   */
  public static  Collection toCollection(OrdinalSet instances) {
    return Iterator2Collection.toSet(instances.iterator());
  }

  /**
   * Precondition: the ordinal set mapping has an index for every element of c Convert a "normal" collection to an OrdinalSet, based
   * on the given mapping.
   * 
   * @throws IllegalArgumentException if c is null
   */
  public static  OrdinalSet toOrdinalSet(Collection c, OrdinalSetMapping m) {
    if (c == null) {
      throw new IllegalArgumentException("c is null");
    }
    if (m == null) {
      throw new IllegalArgumentException("m is null");
    }
    MutableSparseIntSet s = MutableSparseIntSet.makeEmpty();
    for (Iterator it = c.iterator(); it.hasNext();) {
      int index = m.getMappedIndex(it.next());
      assert index >= 0;
      s.add(index);
    }
    return new OrdinalSet(s, m);
  }

  public OrdinalSetMapping getMapping() {
    return mapping;
  }

>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
}
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.util.intset;

import java.util.Collection;
import java.util.Iterator;

import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.debug.Assertions;

/**
 * A Set backed by a set of integers.
 */
public class OrdinalSet implements Iterable {

  private final IntSet S;

  private final OrdinalSetMapping mapping;

  @SuppressWarnings("rawtypes")
  private final static OrdinalSet EMPTY = new OrdinalSet();

  @SuppressWarnings("unchecked")
  public static  OrdinalSet empty() {
    return EMPTY;
  }

  private OrdinalSet() {
    S = null;
    mapping = null;
  }

  public OrdinalSet(IntSet S, OrdinalSetMapping mapping) {
    this.S = S;
    this.mapping = mapping;
  }

  public boolean containsAny(OrdinalSet that) {
    if (that == null) {
      throw new IllegalArgumentException("null that");
    }
    if (S == null || that.S == null) {
      return false;
    }
    return S.containsAny(that.S);
  }

  public int size() {
    return (S == null) ? 0 : S.size();
  }

  public Iterator iterator() {
    if (S == null) {
      return EmptyIterator.instance();
    } else {

      return new Iterator() {
        IntIterator it = S.intIterator();

        public boolean hasNext() {
          return it.hasNext();
        }

        public T next() {
          return mapping.getMappedObject(it.next());
        }

        public void remove() {
          Assertions.UNREACHABLE();
        }
      };
    }
  }

  /**
   * @return a new OrdinalSet instances
   * @throws IllegalArgumentException if A is null
   */
  public static  OrdinalSet intersect(OrdinalSet A, OrdinalSet B) {
    if (A == null) {
      throw new IllegalArgumentException("A is null");
    }
    if (A.size() != 0 && B.size() != 0) {
      assert A.mapping.equals(B.mapping);
    }
    if (A.S == null || B.S == null) {
      return new OrdinalSet(null, A.mapping);
    }
    IntSet isect = A.S.intersection(B.S);
    return new OrdinalSet(isect, A.mapping);
  }

/** BEGIN Custom change: equals for OrdinalSets */
  /**
   * @return true if the contents of two sets are equal
   */
  public static  boolean equals(OrdinalSet a, OrdinalSet b) {
    if (a == null && b == null) {
      return true;
    }
    
    if (a != null && b != null && a.size() == b.size()) {
      if (a.mapping == b.mapping || (a.mapping != null && b.mapping != null && a.mapping.equals(b.mapping))) {
        return a.S == b.S || (a.S != null && b.S != null && a.S.sameValue(b.S));
      }
    }
    
    return false;
  }
  
/** END Custom change: equals for OrdinalSets */
  /**
   * Creates the union of two ordinal sets.
   * 
   * @param A ordinal set a
   * @param B ordinal set b
   * @return union of a and b
   * @throws IllegalArgumentException iff A or B is null
   */
  public static  OrdinalSet unify(OrdinalSet A, OrdinalSet B) {
    if (A == null) {
      throw new IllegalArgumentException("A is null");
    }
    if (B == null) {
      throw new IllegalArgumentException("B is null");
    }
    if (A.size() != 0 && B.size() != 0) {
      assert A.mapping.equals(B.mapping);
    }

    if (A.S == null) {
      return (B.S == null) ? OrdinalSet. empty() : new OrdinalSet(B.S, B.mapping);
    } else if (B.S == null) {
      return new OrdinalSet(A.S, A.mapping);
    }

    IntSet union = A.S.union(B.S);
    return new OrdinalSet(union, A.mapping);
  }

  @Override
  public String toString() {
    return Iterator2Collection.toSet(iterator()).toString();
  }

  /**
   */
  public SparseIntSet makeSparseCopy() {
    return (S == null) ? new SparseIntSet() : new SparseIntSet(S);
  }

  /**
   * Dangerous. Added for performance reasons. Use this only if you really know what you are doing.
   */
  public IntSet getBackingSet() {
    return S;
  }

  /**
   * @return true iff this set contains object
   */
  public boolean contains(T object) {
    if (this == EMPTY || S == null || object == null) {
      return false;
    }
    int index = mapping.getMappedIndex(object);
    return (index == -1) ? false : S.contains(index);
  }

  public boolean isEmpty() {
    return size() == 0;
  }

  /**
   * @throws NullPointerException if instances is null
   */
  public static  Collection toCollection(OrdinalSet instances) {
    return Iterator2Collection.toSet(instances.iterator());
  }

  /**
   * Precondition: the ordinal set mapping has an index for every element of c Convert a "normal" collection to an OrdinalSet, based
   * on the given mapping.
   * 
   * @throws IllegalArgumentException if c is null
   */
  public static  OrdinalSet toOrdinalSet(Collection c, OrdinalSetMapping m) {
    if (c == null) {
      throw new IllegalArgumentException("c is null");
    }
    if (m == null) {
      throw new IllegalArgumentException("m is null");
    }
    MutableSparseIntSet s = MutableSparseIntSet.makeEmpty();
    for (Iterator it = c.iterator(); it.hasNext();) {
      int index = m.getMappedIndex(it.next());
      assert index >= 0;
      s.add(index);
    }
    return new OrdinalSet(s, m);
  }

  public OrdinalSetMapping getMapping() {
    return mapping;
  }

}
File
OrdinalSet.java
Developer's decision
Version 1
Kind of conflict
Annotation
Attribute
Comment
Import
Method declaration
Method invocation
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.viz;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

import com.ibm.wala.util.WalaException;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.Graph;

/**
 * utilities for interfacing with DOT
 */
public class DotUtil {

  /**
   * possible output formats for dot
   * 
   */
  public static enum DotOutputType {
    PS, SVG, PDF, EPS
  }

  private static DotOutputType outputType = DotOutputType.PDF;
  
  private static int fontSize = 6;
  private static String fontColor = "black";
  private static String fontName = "Arial";

  public static void setOutputType(DotOutputType outType) {
    outputType = outType;
  }

  public static DotOutputType getOutputType() {
    return outputType;
  }

  private static String outputTypeCmdLineParam() {
    switch (outputType) {
    case PS:
      return "-Tps";
    case EPS:
      return "-Teps";
    case SVG:
      return "-Tsvg";
    case PDF:
      return "-Tpdf";
    default:
      Assertions.UNREACHABLE();
      return null;
    }
  }

  /**
   * Some versions of dot appear to croak on long labels. Reduce this if so.
   */
  private final static int MAX_LABEL_LENGTH = Integer.MAX_VALUE;


  /**
   */
  public static  void dotify(Graph g, NodeDecorator labels, String dotFile, String outputFile, String dotExe)
    throws WalaException {
    dotify(g, labels, null, dotFile, outputFile, dotExe);
  }

  public static  void dotify(Graph g, NodeDecorator labels, String title, String dotFile, String outputFile, String dotExe)
      throws WalaException {
    if (g == null) {
      throw new IllegalArgumentException("g is null");
    }
    File f = DotUtil.writeDotFile(g, labels, title, dotFile);
/** BEGIN Custom change: spawn exe only when set */
    if (dotExe != null) {
      spawnDot(dotExe, outputFile, f);
    }
/** END Custom change: spawn exe only when set */
  }

  public static void spawnDot(String dotExe, String outputFile, File dotFile) throws WalaException {
    if (dotFile == null) {
      throw new IllegalArgumentException("dotFile is null");
    }
    String[] cmdarray = { dotExe, outputTypeCmdLineParam(), "-o", outputFile, "-v", dotFile.getAbsolutePath() };
    System.out.println("spawning process " + Arrays.toString(cmdarray));
    BufferedInputStream output = null;
    BufferedInputStream error = null;
    try {
      Process p = Runtime.getRuntime().exec(cmdarray);
      output = new BufferedInputStream(p.getInputStream());
      error = new BufferedInputStream(p.getErrorStream());
      boolean repeat = true;
      while (repeat) {
        try {
          Thread.sleep(500);
        } catch (InterruptedException e1) {
          e1.printStackTrace();
          // just ignore and continue
        }
        if (output.available() > 0) {
          byte[] data = new byte[output.available()];
          int nRead = output.read(data);
          System.err.println("read " + nRead + " bytes from output stream");
        }
        if (error.available() > 0) {
          byte[] data = new byte[error.available()];
          int nRead = error.read(data);
          System.err.println("read " + nRead + " bytes from error stream");
        }
        try {
          p.exitValue();
          // if we get here, the process has terminated
          repeat = false;
          System.out.println("process terminated with exit code " + p.exitValue());
        } catch (IllegalThreadStateException e) {
          // this means the process has not yet terminated.
          repeat = true;
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
      throw new WalaException("IOException in " + DotUtil.class);
    } finally {
      if (output != null) {
        try {
          output.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (error != null) {
        try {
          error.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }

  public static  File writeDotFile(Graph g, NodeDecorator labels, String title, String dotfile) throws WalaException {

    if (g == null) {
      throw new IllegalArgumentException("g is null");
    }
    StringBuffer dotStringBuffer = dotOutput(g, labels, title);

    // retrieve the filename parameter to this component, a String
    if (dotfile == null) {
      throw new WalaException("internal error: null filename parameter");
    }
    try {
      File f = new File(dotfile);
      FileWriter fw = new FileWriter(f);
      fw.write(dotStringBuffer.toString());
      fw.close();
      return f;

    } catch (Exception e) {
      throw new WalaException("Error writing dot file " + dotfile);
    }
  }

  /**
   * @return StringBuffer holding dot output representing G
   * @throws WalaException
   */
  private static  StringBuffer dotOutput(Graph g, NodeDecorator labels, String title) throws WalaException {
    StringBuffer result = new StringBuffer("digraph \"DirectedGraph\" {\n");

    if (title != null) {
      result.append("graph [label = \""+title+"\", labelloc=t, concentrate = true];");
    } else {
      result.append("graph [concentrate = true];");
    }
    
    String rankdir = getRankDir();
    if (rankdir != null) {
      result.append("rankdir=" + rankdir + ";");
    }
    String fontsizeStr = "fontsize=" + fontSize;
    String fontcolorStr = (fontColor != null) ? ",fontcolor="+fontColor : "";
    String fontnameStr = (fontName != null) ? ",fontname="+fontName : "";
         
    result.append("center=true;");
    result.append(fontsizeStr);
    result.append(";node [ color=blue,shape=\"box\"");
    result.append(fontsizeStr);
    result.append(fontcolorStr);
    result.append(fontnameStr);
    result.append("];edge [ color=black,");
    result.append(fontsizeStr);
    result.append(fontcolorStr);
    result.append(fontnameStr);
    result.append("]; \n");

    Collection dotNodes = computeDotNodes(g);

    outputNodes(labels, result, dotNodes);

    for (Iterator it = g.iterator(); it.hasNext();) {
      T n = it.next();
      for (Iterator it2 = g.getSuccNodes(n); it2.hasNext();) {
        T s = it2.next();
        result.append(" ");
        result.append(getPort(n, labels));
        result.append(" -> ");
        result.append(getPort(s, labels));
        result.append(" \n");
      }
    }

    result.append("\n}");
    return result;
  }

  private static void outputNodes(NodeDecorator labels, StringBuffer result, Collection dotNodes) throws WalaException {
    for (Iterator it = dotNodes.iterator(); it.hasNext();) {
      outputNode(labels, result, it.next());
    }
  }

  private static void outputNode(NodeDecorator labels, StringBuffer result, Object n) throws WalaException {
    result.append("   ");
    result.append("\"");
    result.append(getLabel(n, labels));
    result.append("\"");
    result.append(decorateNode(n, labels));
  }

  /**
   * Compute the nodes to visualize
   */
  private static  Collection computeDotNodes(Graph g) throws WalaException {
    return Iterator2Collection.toSet(g.iterator());
  }

  private static String getRankDir() throws WalaException {
    return null;
  }

  /**
   * @param n node to decorate
   * @param d decorating master
   */
  private static String decorateNode(Object n, NodeDecorator d) throws WalaException {
    StringBuffer result = new StringBuffer();
    result.append(" [ ]\n");
    return result.toString();
  }

  private static String getLabel(Object o, NodeDecorator d) throws WalaException {
    String result = null;
    if (d == null) {
      result = o.toString();
    } else {
      result = d.getLabel(o);
      result = result == null ? o.toString() : result;
    }
    if (result.length() >= MAX_LABEL_LENGTH) {
      result = result.substring(0, MAX_LABEL_LENGTH - 3) + "...";
    }
    return result;
  }

  private static String getPort(Object o, NodeDecorator d) throws WalaException {
    return "\"" + getLabel(o, d) + "\"";

  }

  public static int getFontSize() {
    return fontSize;
  }

  public static void setFontSize(int fontSize) {
    DotUtil.fontSize = fontSize;
  }

}
=======
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.viz;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

import com.ibm.wala.util.WalaException;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.Graph;

/**
 * utilities for interfacing with DOT
 */
public class DotUtil {

  /**
   * possible output formats for dot
   * 
   */
  public static enum DotOutputType {
    PS, SVG, PDF, EPS
  }

  private static DotOutputType outputType = DotOutputType.PDF;
  
  private static int fontSize = 6;
  private static String fontColor = "black";
  private static String fontName = "Arial";

  public static void setOutputType(DotOutputType outType) {
    outputType = outType;
  }

  public static DotOutputType getOutputType() {
    return outputType;
  }

  private static String outputTypeCmdLineParam() {
    switch (outputType) {
    case PS:
      return "-Tps";
    case EPS:
      return "-Teps";
    case SVG:
      return "-Tsvg";
    case PDF:
      return "-Tpdf";
    default:
      Assertions.UNREACHABLE();
      return null;
    }
  }

  /**
   * Some versions of dot appear to croak on long labels. Reduce this if so.
   */
  private final static int MAX_LABEL_LENGTH = Integer.MAX_VALUE;


  /**
   */
  public static  void dotify(Graph g, NodeDecorator labels, String dotFile, String outputFile, String dotExe)
    throws WalaException {
    dotify(g, labels, null, dotFile, outputFile, dotExe);
  }

  public static  void dotify(Graph g, NodeDecorator labels, String title, String dotFile, String outputFile, String dotExe)
      throws WalaException {
    if (g == null) {
      throw new IllegalArgumentException("g is null");
    }
    File f = DotUtil.writeDotFile(g, labels, title, dotFile);
    spawnDot(dotExe, outputFile, f);
  }

  public static void spawnDot(String dotExe, String outputFile, File dotFile) throws WalaException {
    if (dotFile == null) {
      throw new IllegalArgumentException("dotFile is null");
    }
    String[] cmdarray = { dotExe, outputTypeCmdLineParam(), "-o", outputFile, "-v", dotFile.getAbsolutePath() };
    System.out.println("spawning process " + Arrays.toString(cmdarray));
    BufferedInputStream output = null;
    BufferedInputStream error = null;
    try {
      Process p = Runtime.getRuntime().exec(cmdarray);
      output = new BufferedInputStream(p.getInputStream());
      error = new BufferedInputStream(p.getErrorStream());
      boolean repeat = true;
      while (repeat) {
        try {
          Thread.sleep(500);
        } catch (InterruptedException e1) {
          e1.printStackTrace();
          // just ignore and continue
        }
        if (output.available() > 0) {
          byte[] data = new byte[output.available()];
          int nRead = output.read(data);
          System.err.println("read " + nRead + " bytes from output stream");
        }
        if (error.available() > 0) {
          byte[] data = new byte[error.available()];
          int nRead = error.read(data);
          System.err.println("read " + nRead + " bytes from error stream");
        }
        try {
          p.exitValue();
          // if we get here, the process has terminated
          repeat = false;
          System.out.println("process terminated with exit code " + p.exitValue());
        } catch (IllegalThreadStateException e) {
          // this means the process has not yet terminated.
          repeat = true;
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
      throw new WalaException("IOException in " + DotUtil.class);
    } finally {
      if (output != null) {
        try {
          output.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (error != null) {
        try {
          error.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }

  public static  File writeDotFile(Graph g, NodeDecorator labels, String title, String dotfile) throws WalaException {

    if (g == null) {
      throw new IllegalArgumentException("g is null");
    }
    StringBuffer dotStringBuffer = dotOutput(g, labels, title);

    // retrieve the filename parameter to this component, a String
    if (dotfile == null) {
      throw new WalaException("internal error: null filename parameter");
    }
    try {
      File f = new File(dotfile);
      FileWriter fw = new FileWriter(f);
      fw.write(dotStringBuffer.toString());
      fw.close();
      return f;

    } catch (Exception e) {
      throw new WalaException("Error writing dot file " + dotfile);
    }
  }

  /**
   * @return StringBuffer holding dot output representing G
   * @throws WalaException
   */
  private static  StringBuffer dotOutput(Graph g, NodeDecorator labels, String title) throws WalaException {
    StringBuffer result = new StringBuffer("digraph \"DirectedGraph\" {\n");

    if (title != null) {
      result.append("graph [label = \""+title+"\", labelloc=t, concentrate = true];");
    } else {
      result.append("graph [concentrate = true];");
    }
    
    String rankdir = getRankDir();
    if (rankdir != null) {
      result.append("rankdir=" + rankdir + ";");
    }
    String fontsizeStr = "fontsize=" + fontSize;
    String fontcolorStr = (fontColor != null) ? ",fontcolor="+fontColor : "";
    String fontnameStr = (fontName != null) ? ",fontname="+fontName : "";
         
    result.append("center=true;");
    result.append(fontsizeStr);
    result.append(";node [ color=blue,shape=\"box\"");
    result.append(fontsizeStr);
    result.append(fontcolorStr);
    result.append(fontnameStr);
    result.append("];edge [ color=black,");
    result.append(fontsizeStr);
    result.append(fontcolorStr);
    result.append(fontnameStr);
    result.append("]; \n");

    Collection dotNodes = computeDotNodes(g);

    outputNodes(labels, result, dotNodes);

    for (Iterator it = g.iterator(); it.hasNext();) {
      T n = it.next();
      for (Iterator it2 = g.getSuccNodes(n); it2.hasNext();) {
        T s = it2.next();
        result.append(" ");
        result.append(getPort(n, labels));
        result.append(" -> ");
        result.append(getPort(s, labels));
        result.append(" \n");
      }
    }

    result.append("\n}");
    return result;
  }

  private static void outputNodes(NodeDecorator labels, StringBuffer result, Collection dotNodes) throws WalaException {
    for (Iterator it = dotNodes.iterator(); it.hasNext();) {
      outputNode(labels, result, it.next());
    }
  }

  private static void outputNode(NodeDecorator labels, StringBuffer result, Object n) throws WalaException {
    result.append("   ");
    result.append("\"");
    result.append(getLabel(n, labels));
    result.append("\"");
    result.append(decorateNode(n, labels));
  }

  /**
   * Compute the nodes to visualize
   */
  private static  Collection computeDotNodes(Graph g) throws WalaException {
    return Iterator2Collection.toSet(g.iterator());
  }

  private static String getRankDir() throws WalaException {
    return null;
  }

  /**
   * @param n node to decorate
   * @param d decorating master
   */
  private static String decorateNode(Object n, NodeDecorator d) throws WalaException {
    StringBuffer result = new StringBuffer();
    result.append(" [ ]\n");
    return result.toString();
  }

  private static String getLabel(Object o, NodeDecorator d) throws WalaException {
    String result = null;
    if (d == null) {
      result = o.toString();
    } else {
      result = d.getLabel(o);
      result = result == null ? o.toString() : result;
    }
    if (result.length() >= MAX_LABEL_LENGTH) {
      result = result.substring(0, MAX_LABEL_LENGTH - 3) + "...";
    }
    return result;
  }

  private static String getPort(Object o, NodeDecorator d) throws WalaException {
    return "\"" + getLabel(o, d) + "\"";

  }

  public static int getFontSize() {
    return fontSize;
  }

  public static void setFontSize(int fontSize) {
    DotUtil.fontSize = fontSize;
  }

}
>>>>>>> d5dc65268ee25fd126fcb704233397a4a9086dc6
Solution content
/*******************************************************************************
 * Copyright (c) 2002 - 2006 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.viz;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

import com.ibm.wala.util.WalaException;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.Graph;

/**
 * utilities for interfacing with DOT
 */
public class DotUtil {

  /**
   * possible output formats for dot
   * 
   */
  public static enum DotOutputType {
    PS, SVG, PDF, EPS
  }

  private static DotOutputType outputType = DotOutputType.PDF;
  
  private static int fontSize = 6;
  private static String fontColor = "black";
  private static String fontName = "Arial";

  public static void setOutputType(DotOutputType outType) {
    outputType = outType;
  }

  public static DotOutputType getOutputType() {
    return outputType;
  }

  private static String outputTypeCmdLineParam() {
    switch (outputType) {
    case PS:
      return "-Tps";
    case EPS:
      return "-Teps";
    case SVG:
      return "-Tsvg";
    case PDF:
      return "-Tpdf";
    default:
      Assertions.UNREACHABLE();
      return null;
    }
  }

  /**
   * Some versions of dot appear to croak on long labels. Reduce this if so.
   */
  private final static int MAX_LABEL_LENGTH = Integer.MAX_VALUE;


  /**
   */
  public static  void dotify(Graph g, NodeDecorator labels, String dotFile, String outputFile, String dotExe)
    throws WalaException {
    dotify(g, labels, null, dotFile, outputFile, dotExe);
  }

  public static  void dotify(Graph g, NodeDecorator labels, String title, String dotFile, String outputFile, String dotExe)
      throws WalaException {
    if (g == null) {
      throw new IllegalArgumentException("g is null");
    }
    File f = DotUtil.writeDotFile(g, labels, title, dotFile);
/** BEGIN Custom change: spawn exe only when set */
    if (dotExe != null) {
      spawnDot(dotExe, outputFile, f);
    }
/** END Custom change: spawn exe only when set */
  }

  public static void spawnDot(String dotExe, String outputFile, File dotFile) throws WalaException {
    if (dotFile == null) {
      throw new IllegalArgumentException("dotFile is null");
    }
    String[] cmdarray = { dotExe, outputTypeCmdLineParam(), "-o", outputFile, "-v", dotFile.getAbsolutePath() };
    System.out.println("spawning process " + Arrays.toString(cmdarray));
    BufferedInputStream output = null;
    BufferedInputStream error = null;
    try {
      Process p = Runtime.getRuntime().exec(cmdarray);
      output = new BufferedInputStream(p.getInputStream());
      error = new BufferedInputStream(p.getErrorStream());
      boolean repeat = true;
      while (repeat) {
        try {
          Thread.sleep(500);
        } catch (InterruptedException e1) {
          e1.printStackTrace();
          // just ignore and continue
        }
        if (output.available() > 0) {
          byte[] data = new byte[output.available()];
          int nRead = output.read(data);
          System.err.println("read " + nRead + " bytes from output stream");
        }
        if (error.available() > 0) {
          byte[] data = new byte[error.available()];
          int nRead = error.read(data);
          System.err.println("read " + nRead + " bytes from error stream");
        }
        try {
          p.exitValue();
          // if we get here, the process has terminated
          repeat = false;
          System.out.println("process terminated with exit code " + p.exitValue());
        } catch (IllegalThreadStateException e) {
          // this means the process has not yet terminated.
          repeat = true;
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
      throw new WalaException("IOException in " + DotUtil.class);
    } finally {
      if (output != null) {
        try {
          output.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (error != null) {
        try {
          error.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }

  public static  File writeDotFile(Graph g, NodeDecorator labels, String title, String dotfile) throws WalaException {

    if (g == null) {
      throw new IllegalArgumentException("g is null");
    }
    StringBuffer dotStringBuffer = dotOutput(g, labels, title);

    // retrieve the filename parameter to this component, a String
    if (dotfile == null) {
      throw new WalaException("internal error: null filename parameter");
    }
    try {
      File f = new File(dotfile);
      FileWriter fw = new FileWriter(f);
      fw.write(dotStringBuffer.toString());
      fw.close();
      return f;

    } catch (Exception e) {
      throw new WalaException("Error writing dot file " + dotfile);
    }
  }

  /**
   * @return StringBuffer holding dot output representing G
   * @throws WalaException
   */
  private static  StringBuffer dotOutput(Graph g, NodeDecorator labels, String title) throws WalaException {
    StringBuffer result = new StringBuffer("digraph \"DirectedGraph\" {\n");

    if (title != null) {
      result.append("graph [label = \""+title+"\", labelloc=t, concentrate = true];");
    } else {
      result.append("graph [concentrate = true];");
    }
    
    String rankdir = getRankDir();
    if (rankdir != null) {
      result.append("rankdir=" + rankdir + ";");
    }
    String fontsizeStr = "fontsize=" + fontSize;
    String fontcolorStr = (fontColor != null) ? ",fontcolor="+fontColor : "";
    String fontnameStr = (fontName != null) ? ",fontname="+fontName : "";
         
    result.append("center=true;");
    result.append(fontsizeStr);
    result.append(";node [ color=blue,shape=\"box\"");
    result.append(fontsizeStr);
    result.append(fontcolorStr);
    result.append(fontnameStr);
    result.append("];edge [ color=black,");
    result.append(fontsizeStr);
    result.append(fontcolorStr);
    result.append(fontnameStr);
    result.append("]; \n");

    Collection dotNodes = computeDotNodes(g);

    outputNodes(labels, result, dotNodes);

    for (Iterator it = g.iterator(); it.hasNext();) {
      T n = it.next();
      for (Iterator it2 = g.getSuccNodes(n); it2.hasNext();) {
        T s = it2.next();
        result.append(" ");
        result.append(getPort(n, labels));
        result.append(" -> ");
        result.append(getPort(s, labels));
        result.append(" \n");
      }
    }

    result.append("\n}");
    return result;
  }

  private static void outputNodes(NodeDecorator labels, StringBuffer result, Collection dotNodes) throws WalaException {
    for (Iterator it = dotNodes.iterator(); it.hasNext();) {
      outputNode(labels, result, it.next());
    }
  }

  private static void outputNode(NodeDecorator labels, StringBuffer result, Object n) throws WalaException {
    result.append("   ");
    result.append("\"");
    result.append(getLabel(n, labels));
    result.append("\"");
    result.append(decorateNode(n, labels));
  }

  /**
   * Compute the nodes to visualize
   */
  private static  Collection computeDotNodes(Graph g) throws WalaException {
    return Iterator2Collection.toSet(g.iterator());
  }

  private static String getRankDir() throws WalaException {
    return null;
  }

  /**
   * @param n node to decorate
   * @param d decorating master
   */
  private static String decorateNode(Object n, NodeDecorator d) throws WalaException {
    StringBuffer result = new StringBuffer();
    result.append(" [ ]\n");
    return result.toString();
  }

  private static String getLabel(Object o, NodeDecorator d) throws WalaException {
    String result = null;
    if (d == null) {
      result = o.toString();
    } else {
      result = d.getLabel(o);
      result = result == null ? o.toString() : result;
    }
    if (result.length() >= MAX_LABEL_LENGTH) {
      result = result.substring(0, MAX_LABEL_LENGTH - 3) + "...";
    }
    return result;
  }

  private static String getPort(Object o, NodeDecorator d) throws WalaException {
    return "\"" + getLabel(o, d) + "\"";

  }

  public static int getFontSize() {
    return fontSize;
  }

  public static void setFontSize(int fontSize) {
    DotUtil.fontSize = fontSize;
  }

}
File
DotUtil.java
Developer's decision
Version 1
Kind of conflict
Class declaration
Comment
Import
Package declaration