/* Soot - a J*va Optimization Framework
* Copyright (C) 2004 Jennifer Lhotak
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package soot.javaToJimple;
import soot.*;
import java.util.*;
import polyglot.ast.ClassDecl;
import polyglot.ast.New;
import polyglot.ast.Node;
import polyglot.types.ConstructorInstance;
import polyglot.util.IdentityKey;
public class InitialResolver implements IInitialResolver {
private polyglot.ast.Node astNode; // source node
private polyglot.frontend.Compiler compiler;
private BiMap anonClassMap; // maps New to SootClass (name)
private HashMap<IdentityKey, String> anonTypeMap; //maps polyglot types to soot types
private BiMap localClassMap; // maps LocalClassDecl to SootClass (name)
private HashMap<IdentityKey, String> localTypeMap; // maps polyglot types to soot types
private int privateAccessCounter = 0; // global for whole program because
// the methods created are static
private HashMap<IdentityKey, AnonLocalClassInfo> finalLocalInfo; // new or lcd mapped to list of final locals avail in current meth and the whether its static
private HashMap<String, Node> sootNameToAST = null;
private ArrayList hasOuterRefInInit; // list of sootclass types that need an outer class this param in for init
private HashMap<String, String> classToSourceMap;
private HashMap<SootClass, SootClass> specialAnonMap;
private HashMap<IdentityKey, SootMethod> privateFieldGetAccessMap;
private HashMap<IdentityKey, SootMethod> privateFieldSetAccessMap;
private HashMap<IdentityKey, SootMethod> privateMethodGetAccessMap;
private ArrayList<String> interfacesList;
private ArrayList<Node> cCallList;
private HashMap<New, ConstructorInstance> anonConstructorMap;
public void addToAnonConstructorMap(polyglot.ast.New anonNew, polyglot.types.ConstructorInstance ci){
if (anonConstructorMap == null){
anonConstructorMap = new HashMap<New, ConstructorInstance>();
}
anonConstructorMap.put(anonNew, ci);
}
public polyglot.types.ConstructorInstance getConstructorForAnon(polyglot.ast.New anonNew){
if (anonConstructorMap == null) return null;
return anonConstructorMap.get(anonNew);
}
private FastHierarchy hierarchy;
private AbstractJBBFactory jbbFactory = new JimpleBodyBuilderFactory();
public void setJBBFactory(AbstractJBBFactory jbbFactory){
this.jbbFactory = jbbFactory;
}
public AbstractJBBFactory getJBBFactory(){
return jbbFactory;
}
/**
* returns true if there is an AST avail for given soot class
*/
public boolean hasASTForSootName(String name){
if (sootNameToAST == null) return false;
if (sootNameToAST.containsKey(name)) return true;
return false;
}
/**
* sets AST for given soot class if possible
*/
public void setASTForSootName(String name){
if (!hasASTForSootName(name)) {
throw new RuntimeException("Can only set AST for name if it exists. You should probably not be calling this method unless you know what you're doing!");
}
setAst(sootNameToAST.get(name));
}
public InitialResolver(soot.Singletons.Global g){}
public static InitialResolver v() {
return soot.G.v().soot_javaToJimple_InitialResolver();
}
/**
* Invokes polyglot and gets the AST for the source given in fullPath
*/
public void formAst(String fullPath, List<String> locations, String className){
JavaToJimple jtj = new JavaToJimple();
polyglot.frontend.ExtensionInfo extInfo = jtj.initExtInfo(fullPath, locations);
// only have one compiler - for memory issues
if (compiler == null) {
compiler = new polyglot.frontend.Compiler(extInfo);
}
// build ast
astNode = jtj.compile(compiler, fullPath, extInfo);
resolveAST();
}
/**
* if you have a special AST set it here then call resolveFormJavaFile
* on the soot class
*/
public void setAst(polyglot.ast.Node ast) {
astNode = ast;
}
/*
* March 2nd, 2006 Nomair
* Is it okkay get the ast and send it to the ASTMetrics package????
*/
public polyglot.ast.Node getAst(){
return astNode;
}
private void makeASTMap() {
ClassDeclFinder finder = new ClassDeclFinder();
astNode.visit(finder);
Iterator<ClassDecl> it = finder.declsFound().iterator();
while (it.hasNext()){
polyglot.ast.ClassDecl decl = it.next();
polyglot.types.ClassType type = decl.type();
if (type.flags().isInterface()){
if (interfacesList == null){
interfacesList = new ArrayList<String>();
}
interfacesList.add(Util.getSootType(type).toString());
}
addNameToAST(Util.getSootType(type).toString());
}
}
/**
* add name to AST to map - used mostly for inner and non public
* top-level classes
*/
protected void addNameToAST(String name){
if (sootNameToAST == null){
sootNameToAST = new HashMap<String, Node>();
}
sootNameToAST.put(name, astNode);
}
public void resolveAST(){
buildInnerClassInfo();
if (astNode instanceof polyglot.ast.SourceFile) {
createClassToSourceMap((polyglot.ast.SourceFile)astNode);
}
}
// resolves all types and deals with .class literals and asserts
public Dependencies resolveFromJavaFile(soot.SootClass sc) {
Dependencies dependencies = new Dependencies();
//conservatively load all to signatures
ClassResolver cr = new ClassResolver(sc, dependencies.typesToSignature);
// create class to source map first
// create source file
if (astNode instanceof polyglot.ast.SourceFile) {
cr.createSource((polyglot.ast.SourceFile)astNode);
}
cr.addSourceFileTag(sc);
makeASTMap();
return dependencies;
}
private void createClassToSourceMap(polyglot.ast.SourceFile src){
String srcName = src.source().path();
String srcFileName = null;
if (src.package_() != null){
String slashedPkg = soot.util.StringTools.replaceAll(src.package_().package_().fullName(), ".", System.getProperty("file.separator"));
srcFileName = srcName.substring(srcName.lastIndexOf(slashedPkg));
}
else {
srcFileName = srcName.substring(srcName.lastIndexOf(System.getProperty("file.separator"))+1);
}
ArrayList list = new ArrayList();
Iterator it = src.decls().iterator();
while (it.hasNext()){
polyglot.ast.ClassDecl nextDecl = (polyglot.ast.ClassDecl)it.next();
addToClassToSourceMap(Util.getSootType(nextDecl.type()).toString(), srcFileName);
}
}
private void createLocalAndAnonClassNames(ArrayList<Node> anonBodyList, ArrayList<Node> localClassDeclList){
Iterator<Node> anonBodyIt = anonBodyList.iterator();
while (anonBodyIt.hasNext()){
createAnonClassName((polyglot.ast.New)anonBodyIt.next());
}
Iterator<Node> localClassDeclIt = localClassDeclList.iterator();
while (localClassDeclIt.hasNext()){
createLocalClassName((polyglot.ast.LocalClassDecl)localClassDeclIt.next());
}
}
protected int getNextAnonNum(){
if (anonTypeMap == null) return 1;
else return anonTypeMap.size()+1;
}
private void createAnonClassName(polyglot.ast.New nextNew){
// maybe this anon has already been resolved
if (anonClassMap == null){
anonClassMap = new BiMap();
}
if (anonTypeMap == null){
anonTypeMap = new HashMap<IdentityKey, String>();
}
if (!anonClassMap.containsKey(nextNew)){
int nextAvailNum = 1;
polyglot.types.ClassType outerToMatch = nextNew.anonType().outer();
while (outerToMatch.isNested()){
outerToMatch = outerToMatch.outer();
}
if (!anonTypeMap.isEmpty()){
Iterator<IdentityKey> matchIt = anonTypeMap.keySet().iterator();
while (matchIt.hasNext()){
polyglot.types.ClassType pType = (polyglot.types.ClassType)matchIt.next().object();
polyglot.types.ClassType outerMatch = pType.outer();
while (outerMatch.isNested()){
outerMatch = outerMatch.outer();
}
if (outerMatch.equals(outerToMatch)){
int numFound = getAnonClassNum(anonTypeMap.get(new polyglot.util.IdentityKey(pType)));
if (numFound >= nextAvailNum){
nextAvailNum = numFound+1;
}
}
}
}
String realName = outerToMatch.fullName()+"$"+nextAvailNum;
anonClassMap.put(nextNew, realName);
anonTypeMap.put(new polyglot.util.IdentityKey(nextNew.anonType()), realName);
addNameToAST(realName);
}
}
private void createLocalClassName(polyglot.ast.LocalClassDecl lcd){
// maybe this localdecl has already been resolved
if (localClassMap == null){
localClassMap = new BiMap();
}
if (localTypeMap == null){
localTypeMap = new HashMap<IdentityKey, String>();
}
if (!localClassMap.containsKey(lcd)){
int nextAvailNum = 1;
polyglot.types.ClassType outerToMatch = lcd.decl().type().outer();
while (outerToMatch.isNested()){
outerToMatch = outerToMatch.outer();
}
if (!localTypeMap.isEmpty()){
Iterator<IdentityKey> matchIt = localTypeMap.keySet().iterator();
while (matchIt.hasNext()){
polyglot.types.ClassType pType = (polyglot.types.ClassType)matchIt.next().object();
polyglot.types.ClassType outerMatch = pType.outer();
while (outerMatch.isNested()){
outerMatch = outerMatch.outer();
}
if (outerMatch.equals(outerToMatch)){
int numFound = getLocalClassNum(localTypeMap.get(new polyglot.util.IdentityKey(pType)), lcd.decl().name());
if (numFound >= nextAvailNum){
nextAvailNum = numFound+1;
}
}
}
}
String realName = outerToMatch.fullName()+"$"+nextAvailNum+lcd.decl().name();
localClassMap.put(lcd, realName);
localTypeMap.put(new polyglot.util.IdentityKey(lcd.decl().type()), realName);
addNameToAST(realName);
}
}
private static final int NO_MATCH = 0;
private int getLocalClassNum(String realName, String simpleName){
// a local inner class is named outer$NsimpleName where outer
// is the very outer most class
int dIndex = realName.indexOf("$");
int nIndex = realName.indexOf(simpleName, dIndex);
if (nIndex == -1) return NO_MATCH;
if (dIndex == -1) {
throw new RuntimeException("Matching an incorrectly named local inner class: "+realName);
}
String numString = realName.substring(dIndex+1, nIndex);
for (int i = 0; i < numString.length(); i++){
if (!Character.isDigit(numString.charAt(i))) return NO_MATCH;
}
return (new Integer(numString)).intValue();
}
private int getAnonClassNum(String realName){
// a anon inner class is named outer$N where outer
// is the very outer most class
int dIndex = realName.indexOf("$");
if (dIndex == -1) {
throw new RuntimeException("Matching an incorrectly named anon inner class: "+realName);
}
return (new Integer(realName.substring(dIndex+1))).intValue();
}
/**
* ClassToSourceMap is for classes whos names don't match the source file
* name - ex: multiple top level classes in a single file
*/
private void addToClassToSourceMap(String className, String sourceName) {
if (classToSourceMap == null){
classToSourceMap = new HashMap<String, String>();
}
classToSourceMap.put(className, sourceName);
}
public boolean hasClassInnerTag(soot.SootClass sc, String innerName){
Iterator it = sc.getTags().iterator();
while (it.hasNext()){
soot.tagkit.Tag t = (soot.tagkit.Tag)it.next();
if (t instanceof soot.tagkit.InnerClassTag) {
soot.tagkit.InnerClassTag tag = (soot.tagkit.InnerClassTag)t;
if (tag.getInnerClass().equals(innerName)) return true;
}
}
return false;
}
private void buildInnerClassInfo(){
InnerClassInfoFinder icif = new InnerClassInfoFinder();
astNode.visit(icif);
createLocalAndAnonClassNames(icif.anonBodyList(), icif.localClassDeclList());
buildFinalLocalMap(icif.memberList());
}
private void buildFinalLocalMap(ArrayList<Node> memberList){
Iterator<Node> it = memberList.iterator();
while (it.hasNext()){
handleFinalLocals((polyglot.ast.ClassMember)it.next());
}
}
private void handleFinalLocals(polyglot.ast.ClassMember member){
MethodFinalsChecker mfc = new MethodFinalsChecker();
member.visit(mfc);
//System.out.println("member: "+member);
//System.out.println("mcf final locals avail: "+mfc.finalLocals());
//System.out.println("mcf locals used: "+mfc.typeToLocalsUsed());
//System.out.println("mfc inners: "+mfc.inners());
if (cCallList == null){
cCallList = new ArrayList<Node>();
}
cCallList.addAll(mfc.ccallList());
//System.out.println("cCallList: "+cCallList);
AnonLocalClassInfo alci = new AnonLocalClassInfo();
if (member instanceof polyglot.ast.ProcedureDecl){
polyglot.ast.ProcedureDecl procedure = (polyglot.ast.ProcedureDecl)member;
// not sure if this will break deep nesting
alci.finalLocalsAvail(mfc.finalLocals());
if (procedure.flags().isStatic()){
alci.inStaticMethod(true);
}
}
else if (member instanceof polyglot.ast.FieldDecl){
alci.finalLocalsAvail(new ArrayList<IdentityKey>());
if (((polyglot.ast.FieldDecl)member).flags().isStatic()){
alci.inStaticMethod(true);
}
}
else if (member instanceof polyglot.ast.Initializer){
// for now don't make final locals avail in init blocks
// need to test this
alci.finalLocalsAvail(mfc.finalLocals());
if (((polyglot.ast.Initializer)member).flags().isStatic()){
alci.inStaticMethod(true);
}
}
if (finalLocalInfo == null){
finalLocalInfo = new HashMap<IdentityKey, AnonLocalClassInfo>();
}
Iterator<IdentityKey> it = mfc.inners().iterator();
while (it.hasNext()){
polyglot.types.ClassType cType = (polyglot.types.ClassType)it.next().object();
// do the comparison about locals avail and locals used here
HashMap<IdentityKey, ArrayList<IdentityKey>> typeToLocalUsed = mfc.typeToLocalsUsed();
ArrayList<IdentityKey> localsUsed = new ArrayList<IdentityKey>();
if (typeToLocalUsed.containsKey(new polyglot.util.IdentityKey(cType))){
ArrayList localsNeeded = typeToLocalUsed.get(new polyglot.util.IdentityKey(cType));
Iterator usesIt = localsNeeded.iterator();
while (usesIt.hasNext()){
polyglot.types.LocalInstance li = (polyglot.types.LocalInstance)((polyglot.util.IdentityKey)usesIt.next()).object();
if (alci.finalLocalsAvail().contains(new polyglot.util.IdentityKey(li))){
localsUsed.add(new polyglot.util.IdentityKey(li));
}
}
}
AnonLocalClassInfo info = new AnonLocalClassInfo();
info.inStaticMethod(alci.inStaticMethod());
info.finalLocalsAvail(localsUsed);
if (!finalLocalInfo.containsKey(new polyglot.util.IdentityKey(cType))){
finalLocalInfo.put(new polyglot.util.IdentityKey(cType), info);
}
}
}
public boolean isAnonInCCall(polyglot.types.ClassType anonType){
//System.out.println("checking type: "+anonType);
Iterator<Node> it = cCallList.iterator();
while (it.hasNext()){
polyglot.ast.ConstructorCall cCall = (polyglot.ast.ConstructorCall)it.next();
//System.out.println("cCall params: "+cCall.arguments());
Iterator argsIt = cCall.arguments().iterator();
while (argsIt.hasNext()){
Object next = argsIt.next();
if (next instanceof polyglot.ast.New && ((polyglot.ast.New)next).anonType() != null){
//System.out.println("comparing: "+((polyglot.ast.New)next).anonType());
if (((polyglot.ast.New)next).anonType().equals(anonType)) return true;
}
}
}
return false;
}
public BiMap getAnonClassMap(){
return anonClassMap;
}
public BiMap getLocalClassMap(){
return localClassMap;
}
public HashMap<IdentityKey, String> getAnonTypeMap(){
return anonTypeMap;
}
public HashMap<IdentityKey, String> getLocalTypeMap(){
return localTypeMap;
}
public HashMap<IdentityKey, AnonLocalClassInfo> finalLocalInfo(){
return finalLocalInfo;
}
public int getNextPrivateAccessCounter(){
int res = privateAccessCounter;
privateAccessCounter++;
return res;
}
public ArrayList getHasOuterRefInInit(){
return hasOuterRefInInit;
}
public void setHasOuterRefInInit(ArrayList list){
hasOuterRefInInit = list;
}
public HashMap<SootClass, SootClass> specialAnonMap(){
return specialAnonMap;
}
public void setSpecialAnonMap(HashMap<SootClass, SootClass> map){
specialAnonMap = map;
}
public void hierarchy(soot.FastHierarchy fh){
hierarchy = fh;
}
public soot.FastHierarchy hierarchy(){
return hierarchy;
}
private HashMap<SootClass, InnerClassInfo> innerClassInfoMap;
public HashMap<SootClass, InnerClassInfo> getInnerClassInfoMap(){
return innerClassInfoMap;
}
public void setInnerClassInfoMap(HashMap<SootClass, InnerClassInfo> map){
innerClassInfoMap = map;
}
protected HashMap<String, String> classToSourceMap(){
return classToSourceMap;
}
public void addToPrivateFieldGetAccessMap(polyglot.ast.Field field, soot.SootMethod meth){
if (privateFieldGetAccessMap == null){
privateFieldGetAccessMap = new HashMap<IdentityKey, SootMethod>();
}
privateFieldGetAccessMap.put(new polyglot.util.IdentityKey(field.fieldInstance()), meth);
}
public HashMap<IdentityKey, SootMethod> getPrivateFieldGetAccessMap(){
return privateFieldGetAccessMap;
}
public void addToPrivateFieldSetAccessMap(polyglot.ast.Field field, soot.SootMethod meth){
if (privateFieldSetAccessMap == null){
privateFieldSetAccessMap = new HashMap<IdentityKey, SootMethod>();
}
privateFieldSetAccessMap.put(new polyglot.util.IdentityKey(field.fieldInstance()), meth);
}
public HashMap<IdentityKey, SootMethod> getPrivateFieldSetAccessMap(){
return privateFieldSetAccessMap;
}
public void addToPrivateMethodGetAccessMap(polyglot.ast.Call call, soot.SootMethod meth){
if (privateMethodGetAccessMap == null){
privateMethodGetAccessMap = new HashMap<IdentityKey, SootMethod>();
}
privateMethodGetAccessMap.put(new polyglot.util.IdentityKey(call.methodInstance()), meth);
}
public HashMap<IdentityKey, SootMethod> getPrivateMethodGetAccessMap(){
return privateMethodGetAccessMap;
}
public ArrayList<String> getInterfacesList() {
return interfacesList;
}
}