Package dk.brics.string.java

Source Code of dk.brics.string.java.Jimple2Intermediate

package dk.brics.string.java;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import soot.ArrayType;
import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.Hierarchy;
import soot.IntType;
import soot.LongType;
import soot.NullType;
import soot.PrimType;
import soot.RefType;
import soot.Scene;
import soot.ShortType;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.Type;
import soot.Value;
import soot.ValueBox;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.ParameterRef;
import dk.brics.automaton.Automaton;
import dk.brics.string.MethodNameTaintAnalysisStrategy;
import dk.brics.string.StringAnalysis;
import dk.brics.string.external.ExternalVisibility;
import dk.brics.string.external.Resolver;
import dk.brics.string.intermediate.Application;
import dk.brics.string.intermediate.ArrayCorrupt;
import dk.brics.string.intermediate.AssertStatement;
import dk.brics.string.intermediate.Call;
import dk.brics.string.intermediate.FieldAssignment;
import dk.brics.string.intermediate.Method;
import dk.brics.string.intermediate.Nop;
import dk.brics.string.intermediate.ObjectCorrupt;
import dk.brics.string.intermediate.PrimitiveInit;
import dk.brics.string.intermediate.Return;
import dk.brics.string.intermediate.Statement;
import dk.brics.string.intermediate.StringAssignment;
import dk.brics.string.intermediate.StringBufferCorrupt;
import dk.brics.string.intermediate.StringInit;
import dk.brics.string.intermediate.StringStatement;
import dk.brics.string.intermediate.Variable;
import dk.brics.string.intermediate.VariableType;
import dk.brics.string.stringoperations.Basic;

/**
* Converter from Jimple code to intermediate representation.
* <p/>
* Basic rundown of the translation process:
* <ol>
* <li>Create intermediate {@link Method} objects with empty bodies for each
* method in the application.
* <li>Create the <i>wrapper</i> method, which is a special method that calls
* all externally visible methods with corrupt/unknown arguments.
* <li>Create methods modelling the <tt>toString</tt>-method for each
* application class.
* <li>Translate the method bodies from jimple into intermediate code.
* </ol>
* The first three steps are handled by this class, while the fourth and most
* complicated step is delegated to several other classes. See
* {@link MethodTranslator} for an overview how how step four is executed.
*
* @see dk.brics.string.StringAnalysis
* @see MethodTranslator
*/
public class Jimple2Intermediate implements TranslationContext {

    /**
     * Methods being generated.
     */
    private List<Method> methods = new LinkedList<Method>();

    /**
     * Map from signatures to methods.
     */
    private Map<String, Method> sms_m = new HashMap<String, Method>();

    /**
     * For each method signature, map from real-arg number to relevant-arg
     * number.
     */
    private Map<String, int[]> sms_sa_ma = new HashMap<String, int[]>();

    /**
     * For each method signature, map from relevant-arg number to real-arg
     * number.
     */
    private Map<String, int[]> sms_ma_sa = new HashMap<String, int[]>();

    /**
     * Defines which methods are accessible from external code.
     */
    private ExternalVisibility ext;

    /**
     * The taint analysis strategy for determining taint strings
     */
    private TaintAnalysisStrategy taintAnalysisStrategy = new MethodNameTaintAnalysisStrategy(
            Collections.<String> emptyList());

    /**
     * Hotspots (all expressions if null).
     */
    private Collection<ValueBox> hotspots;

    /**
     * Map from classes to their <code>toString</code> methods.
     */
    private Map<SootClass, Method> tostring_targets = new HashMap<SootClass, Method>();

    /**
     * Map from names of a classes to their <code>toString</code> methods.
     */
    private Map<String, Method> tostring_methods = new HashMap<String, Method>();

    /**
     * Hotspots for <code>toString</code> methods. Used by
     * {@link StringAnalysis#getTypeAutomaton(VariableType)}.
     */
    private Map<SootClass, StringStatement> tostring_hotspots = new HashMap<SootClass, StringStatement>();

    /**
     * Cache of automata for primitive types and null type.
     */
    private Map<Type, Automaton> type_automaton = new HashMap<Type, Automaton>();

    /**
     * Class hierarchy, provided by Soot.
     */
    private Hierarchy class_hierarchy;

    /**
     * Map from Jimple expression to intermediate representation statement.
     */
    private Map<ValueBox, Statement> trans_map = new HashMap<ValueBox, Statement>();

    /**
     * Map from Jimple expression to source file name.
     */
    private Map<ValueBox, String> sourcefile_map = new HashMap<ValueBox, String>();

    /**
     * Map from Jimple expression to class name.
     */
    private Map<ValueBox, String> class_map = new HashMap<ValueBox, String>();

    /**
     * Map from Jimple expression to method name.
     */
    private Map<ValueBox, String> method_map = new HashMap<ValueBox, String>();

    /**
     * Map from Jimple expression to line number.
     */
    private Map<ValueBox, Integer> line_map = new HashMap<ValueBox, Integer>();
   
    private Map<Value, Variable> expression_map = new HashMap<Value, Variable>();

    private Logger log = Logger.getLogger(Jimple2Intermediate.class); // FIXME: check uses of log4j ?!

    private VariableManager variableManager;
   
    private AbstractDispatchStrategy abstractDispatchStrategy;
   
    private StaticStringTypes staticStringTypes;
   
    /**
     * Number of skipped string expressions. String constants are skipped if
     * arguments to non-application class methods that are not hotspots.
     */
    int skipped;

    private MethodTranslator methodTranslator;

    private Application application;
   
    private Collection<? extends Resolver> resolvers;

    /**
     * Constructs a new converter using no resolvers.
     */
    @Deprecated
    public Jimple2Intermediate(ExternalVisibility ext) {
        this(new Jimple2IntermediateFactoryImpl(ext, Collections.<Resolver> emptySet()));
    }

    /**
     * Constructs a new converter using the given resolvers.
     */
    @Deprecated
    public Jimple2Intermediate(ExternalVisibility ext,
            Collection<? extends Resolver> resolvers) {
        this(new Jimple2IntermediateFactoryImpl(ext, resolvers));
    }

    @Deprecated
    public Jimple2Intermediate(TaintAnalysisStrategy taintAnalysisStrategy,
            Collection<? extends Resolver> resolvers, ExternalVisibility ext) {
        this(new Jimple2IntermediateFactoryImpl(ext, taintAnalysisStrategy, resolvers));
    }

    public Jimple2Intermediate(Jimple2IntermediateFactory af) {
        this.taintAnalysisStrategy = af.getTaintAnalysisStrategy();
        this.ext = af.getExternallyVisible();
        this.methodTranslator = af.getMethodTranslator();
        this.staticStringTypes = af.getStaticStringTypes();
        this.resolvers = af.getResolvers();
    }
   
    /**
     * Translates the application classes considering all expressions as
     * hotspots.
     */
    public Application translateApplicationClasses() {
        return translateApplicationClasses(null);
    }
   
    /**
     * Translates the application classes considering the given expressions as
     * hotspots.
     */
    public Application translateApplicationClasses(Collection<ValueBox> hotspots) {
        applicationClasses = new ArrayList<SootClass>();
        for (SootClass clazz : Scene.v().getApplicationClasses()) {
            if (!clazz.isPhantom() && !clazz.isPhantomClass()) {// no idea what the difference between these two are
                applicationClasses.add(clazz);
            }
        }
       
        this.application = new Application();
        this.variableManager = new VariableManager(application, this);
        log.info("Translating classes to intermediate form...");
        this.hotspots = hotspots;
       
        // load ALL classes we might need before creating the hierarchy!!
        Scene.v().getSootClass("java.util.Iterator");
        Scene.v().getSootClass("java.util.Collection");
        class_hierarchy = new Hierarchy();
        this.abstractDispatchStrategy = new AbstractDispatchClassHierarchy(class_hierarchy);
        skipped = 0;
        makeMethods();
        makeWrapperMethod();
        makeToStringMethods();
        translate();
        removeNops();
        return application;
    }

    public boolean isHotspot(ValueBox expr) {
        return hotspots == null || hotspots.contains(expr);
    }

    /**
     * Returns the method object for the given signature.
     */
    public Method getMethod(String signature) {
        return sms_m.get(signature);
    }

    /**
     * Returns the relevant-arg numbers for the given method.
     */
    public int[] getRelevantArgNumbers(String signature) {
        return sms_sa_ma.get(signature);
    }

    /**
     * Returns the translation map. <code>translateApplicationClasses</code>
     * must be called first.
     *
     * @return map from Jimple expression to intermediate representation
     *         statement
     */
    public Map<ValueBox, Statement> getTranslationMap() {
        return trans_map;
    }

    /**
     * Returns the number of string constants that have been skipped because
     * they are non-hotspot arguments to non-application class methods.
     */
    public int getNumberOfExpsSkipped() {
        return skipped;
    }

    /**
     * Returns hotspots for <code>toString</code> methods.
     */
    public Map<SootClass, StringStatement> getToStringHotspotMap() {
        return tostring_hotspots;
    }

    /**
     * Returns source file name for each expression.
     */
    public Map<ValueBox, String> getSourceFileMap() {
        return sourcefile_map;
    }

    /**
     * Returns class name for each expression.
     */
    public Map<ValueBox, String> getClassNameMap() {
        return class_map;
    }

    /**
     * Returns method name for each expression.
     */
    public Map<ValueBox, String> getMethodNameMap() {
        return method_map;
    }

    /**
     * Returns line number for each expression.
     */
    public Map<ValueBox, Integer> getLineNumberMap() {
        return line_map;
    }

    /**
     * Prepares <code>Method</code> objects for application classes.
     */
    void makeMethods() {
        for (SootClass sc : getApplicationClasses()) {
            for (SootMethod sm : sc.getMethods()) {
                List<Variable> vars = new LinkedList<Variable>();
                List<Type> params = getParameterTypes(sm);
                int[] sa_ma = new int[params.size()];
                int[] ma_sa = new int[params.size()];
                int ma = 0;
                int sa = 0;
                for (Type pt : params) {
                    // if (isSType(pt)) {
                    if (fromSootType(pt) != VariableType.NONE) {
                        vars.add(makeVariable(pt));
                        sa_ma[sa] = ma;
                        ma_sa[ma] = sa;
                        ma++;
                    } else {
                        sa_ma[sa] = -1;
                    }
                    sa++;
                }
                Variable[] var_array = vars.toArray(new Variable[0]);
                Method m = new Method(application, sm.getName(), var_array);
                methods.add(m);
                sms_m.put(sm.getSignature(), m);
                sms_sa_ma.put(sm.getSignature(), sa_ma);
                sms_ma_sa.put(sm.getSignature(), ma_sa);
            }
        }
    }

    /**
     * Makes <code>toString</code> methods for basic wrapper classes and
     * application classes.
     */
    void makeToStringMethods() {
        // Make basic tostring methods
        makeBasicToStringMethod(null, "java.lang.Object", Basic
                .makeObjectString());
        makeBasicToStringMethod(BooleanType.v(), "java.lang.Boolean", Basic
                .makeBooleanString());
        makeBasicToStringMethod(ByteType.v(), "java.lang.Byte", Basic
                .makeByteString());
        makeBasicToStringMethod(CharType.v(), "java.lang.Character", Basic
                .makeCharacterString());
        makeBasicToStringMethod(DoubleType.v(), "java.lang.Double", Basic
                .makeDoubleString());
        makeBasicToStringMethod(FloatType.v(), "java.lang.Float", Basic
                .makeFloatString());
        makeBasicToStringMethod(IntType.v(), "java.lang.Integer", Basic
                .makeIntegerString());
        makeBasicToStringMethod(LongType.v(), "java.lang.Long", Basic
                .makeLongString());
        makeBasicToStringMethod(ShortType.v(), "java.lang.Short", Basic
                .makeShortString());
       
        // Make tostring methods for application classes
        // Link toString calls to the hotspots for all superclasses of the
        // receiver type
        for (SootClass ac : getApplicationClasses()) {
            Method m = new Method(application, ac.getName() + ".toString",
                    new Variable[0]);
            methods.add(m);
            tostring_methods.put(ac.getName(), m);
            Variable var = application.createVariable(VariableType.STRING);
            StringStatement spot = new StringAssignment(var, var);
            m.addStatement(spot);
            Return ret = new Return(var);
            m.addStatement(ret);
            spot.addSucc(ret);
            tostring_hotspots.put(ac, spot); // these hotspots are used by
                                             // StringAnalysis.getTypeAutomaton
            Collection<SootClass> subtypes;
            if (ac.isInterface())
                subtypes = class_hierarchy.getImplementersOf(ac);
            else
                subtypes = class_hierarchy.getSubclassesOfIncluding(ac);
            for (SootClass ac2 : subtypes) {
                if (ac2.isPhantom() || ac2.isPhantomClass() || ac2.isInterface())
                    continue;
                Method target = tostring_targets.get(ac2);
                if (target == null)
                    continue;
                Call call = new Call(var, target, new Variable[0]);
                m.addStatement(call);
                m.getEntry().addSucc(call);
                call.addSucc(spot);
            }
           
            /*for (SootClass ac2 : getApplicationClasses()) {
                if (ac2.isInterface()) // skip interfaces here
                    continue;
                if (class_hierarchy.isClassSubclassOfIncluding(ac2, ac)) {
                    // Calling tostring with a receiver of type ac,
                    // the method in class ac2 might be called.
                    // ac2 might not implement it directly,
                    // so we search upwards for the implementation
                    while (!tostring_targets.containsKey(ac2)) {
                        ac2 = ac2.getSuperclass();
                    }
                    Method target = tostring_targets.get(ac2);
                    Call call = new Call(var, target, new Variable[0]);
                    m.addStatement(call);
                    m.getEntry().addSucc(call);
                    call.addSucc(spot);
                }
            }*/
        }
    }

    /**
     * Makes a method that simulates the <code>toString</code> method of a basic
     * wrapper classes.
     */
    void makeBasicToStringMethod(Type prim, String classname, Automaton a) {
        Method m = new Method(application, classname + ".toString",
                new Variable[0]);
        SootClass c = Scene.v().getSootClass(classname);
        Variable var = application.createVariable(VariableType.STRING);
        StringStatement ss = new StringInit(var, a);
        m.addStatement(ss);
        m.getEntry().addSucc(ss);
        Return ret = new Return(var);
        m.addStatement(ret);
        ss.addSucc(ret);

        methods.add(m);
        tostring_targets.put(c, m);
        tostring_methods.put(classname, m);
        tostring_hotspots.put(c, ss);

        type_automaton.put(prim, a);
    }

    /**
     * Translates the method bodies.
     */
    void translate() {
        for (SootClass ac : getApplicationClasses()) {
            for (SootMethod sm : ac.getMethods()) {
                if (sm.isConcrete()) {
                    List<HotspotInfo> hotspots = methodTranslator.translateMethod(sm, this);

                    // add all the hotspots we found
                    for (HotspotInfo hot : hotspots) {
                        registerHotspot(hot);
                    }
                }
            }
        }
    }

    /**
     * Makes wrapper method that calls all externally visible methods in
     * application classes, using arbitrary arguments.
     */
    void makeWrapperMethod() {
        Method wrapper = new Method(application, "<wrapper>", new Variable[0]);
        methods.add(wrapper);
       
        // build the wrapper's body
        ControlFlowBuilder cfg = new ControlFlowBuilder(wrapper);
        cfg.moveToStatement(wrapper.getEntry());
       
        // create a variable holding any string
        Variable anyVar = application.createVariable(VariableType.STRING);
        Statement assignAny = new StringInit(anyVar, Basic.makeAnyString());
        cfg.addStatement(assignAny);

        // create a variable holding the null string
        Variable nullVar = application.createVariable(VariableType.STRING);
        Statement assignNull = new StringInit(nullVar, Automatons.getNull());
        cfg.addStatement(assignNull);
       
        // initialize externally visible field variables to anything
        // and set string fields to "null"
        for (SootClass ac : getApplicationClasses()) {
            for (SootField field : ac.getFields()) {
                // String fields should be assigned to "null" because they are
                // exempt from the
                // null-pointer analysis we use for other objects
                if (field.getType().equals(RefType.v("java.lang.String"))) {
                    FieldAssignment assignment = new FieldAssignment(
                            variableManager.getField(field), nullVar);
                    cfg.addStatement(assignment);
                }

                // corrupt externally visible fields
                if (ext.isExternallyVisibleField(field)) {
                    VariableType type = fromSootType(field
                            .getType());

                    if (type == VariableType.NONE)
                        continue;

                    Variable fieldInit;

                    switch (type) {
                    case OBJECT:
                    case STRING:
                    case PRIMITIVE:
                        fieldInit = anyVar;
                        break;

                    case STRINGBUFFER: {
                        fieldInit = application.createVariable(VariableType.STRINGBUFFER);
                        Statement s = new StringBufferCorrupt(fieldInit);
                        cfg.addStatement(s);
                        break;
                    }

                    case ARRAY: {
                        fieldInit = application.createVariable(VariableType.ARRAY);
                        Statement s = new ArrayCorrupt(fieldInit);
                        cfg.addStatement(s);
                        break;
                    }
                    default:
                        throw new RuntimeException("Unknown field type " + type);
                    }// switch

                    FieldAssignment assignment = new FieldAssignment(variableManager.getField(field), fieldInit);
                    cfg.addStatement(assignment);
                }
            }
        }
       
        // split control here, and call a random externally visible method
        cfg.startBranch();
       
        // call externally visible methods
        for (SootClass ac : getApplicationClasses()) {
            for (SootMethod sm : ac.getMethods()) {
                if (ext.isExternallyVisibleMethod(sm)) {
                    Method m = sms_m.get(sm.getSignature());
                    Variable[] params = m.getEntry().params;
                    Variable[] args = new Variable[params.length];
                    for (int i = 0; i < params.length; i++) {
                        Variable arg = application.createVariable(params[i].getType());
                        args[i] = arg;
                        Statement s;
                        switch (arg.getType()) {
                        case STRING:
                            s = new StringInit(arg, Basic.makeAnyString());
                            break;
                        case STRINGBUFFER:
                            s = new StringBufferCorrupt(arg);
                            break;
                        case ARRAY:
                            s = new ArrayCorrupt(arg);
                            break;
                        case PRIMITIVE:
                            // TODO: Integers can contain two characters, right? look deeper into which primitive type
                            s = new PrimitiveInit(arg, Automaton.makeAnyChar());
                            break;
                        default:
                            s = new ObjectCorrupt(arg);
                            // (case NONE or NULL cannot occur because such
                            // parameters do not get created for intermediate
                            // methods)
                        }
                        cfg.addStatement(s);
                    }
                    Variable retvar = makeVariable(sm.getReturnType());
                    Call c = new Call(retvar, m, args);
                    cfg.addStatement(c);
                    // If this is toString, remember the return value
                    if (sm.getName().equals("toString")
                            && sm.getParameterCount() == 0
                            && sm.getReturnType().toString().equals("java.lang.String")) {
                        tostring_targets.put(ac, m);
                    }
                   
                    cfg.useBranch();
                }
            }
        }
       
        // also add the possibility of no method being called.
        // in case the application has no externally visible methods, we still want
        // the return statement to be reachable so we don't create a malformed program.
        cfg.useBranch();
       
        cfg.endBranch();
       
        // add a return statement
        Return ret = new Return(application.createVariable(VariableType.NONE));
        cfg.addStatement(ret);
    }

    /**
     * Removes all <code>Nop</code>s in the generated code.
     */
    void removeNops() {
      Set<Statement> protectedNops = new HashSet<Statement>();
        for (Method m : methods) {
          protectedNops.clear();
          // assertions referring to a nop statement must have their target updated to the nop's predecessor,
          // or in case there are multiple predecessors, the nop must not be removed
          for (Statement s : m.getStatements()) {
            if (s instanceof AssertStatement) {
              AssertStatement a = (AssertStatement)s;
              Statement target = a.targetStatement;
              while (target instanceof Nop && target.getPreds().size() == 1) {
                target = target.getPreds().iterator().next();
                a.targetStatement = target;
              }
              if (target instanceof Nop) {
                protectedNops.add(target);
              }
            }
          }
         
            for (Statement s : new ArrayList<Statement>(m.getStatements())) {
                if (s instanceof Nop && !protectedNops.contains(s)) {
                    m.removeNop((Nop) s);
                }
            }
        }
    }

    /**
     * Makes a new variable, using the given Soot type.
     */
    Variable makeVariable(Type t) {
        return application.createVariable(fromSootType(t));
    }

    /**
     * Returns the type corresponding to the given Soot value.
     */
    VariableType getValueType(Value v) {
        return getType(v.getType());
    }

    /**
     * Returns the type corresponding to the given Soot type.
     */
    VariableType getType(Type type) {
        return fromSootType(type);
    }

    public List<SootMethod> getTargetsOf(InstanceInvokeExpr expr) {
        return abstractDispatchStrategy.getTargetsOf(expr);
    }

    public Method getToStringMethod(SootClass c) {
        return tostring_methods.get(c.getName());
    }

    public Automaton getTypeAutomaton(Type t) {
        if (t instanceof PrimType) {
            return type_automaton.get(t);
        } else if (t instanceof NullType) {
            return Automaton.makeString("null");
        } else if (t instanceof RefType) {
          for (Resolver resolver : resolvers) {
            Automaton at = resolver.resolveToString(((RefType)t).getSootClass());
            if (at != null)
              return at;
          }
          return null;
        } else {
            return null;
        }
    }

    /**
     * Checks whether the given class is an application class.
     */
    public boolean isApplicationClass(SootClass c) {
        for (SootClass ac : getApplicationClasses()) {
            if (c.getName().equals(ac.getName())) {
                return true;
            }
        }
        return false;
    }

    private void registerHotspot(HotspotInfo hotspot) {
        ValueBox box = hotspot.getBox();
        trans_map.put(box, hotspot.getStatement());
        sourcefile_map.put(box, hotspot.getSourcefile());
        class_map.put(box, hotspot.getClassName());
        method_map.put(box, hotspot.getMethodName());
        line_map.put(box, hotspot.getLineNumber());
    }

    /**
     * Returns the parameter variable for the given relevant-arg number in the
     * current method.
     */
    public Variable getParameter(SootMethod sm, int index) {
        int ma = getRelevantArgNumbers(sm.getSignature())[index];
        if (ma != -1) {
            return getMethod(sm.getSignature()).getEntry().params[ma];
        } else {
            return application.createVariable(VariableType.NONE);
        }
    }

    public TaintAnalysisStrategy getTaintAnalysisStrategy() {
        return taintAnalysisStrategy;
    }
   
    private Collection<SootClass> applicationClasses;

    private Collection<SootClass> getApplicationClasses() {
        return applicationClasses;
    }

    @SuppressWarnings("unchecked")
    private List<Type> getParameterTypes(SootMethod sm) {
        return sm.getParameterTypes();
    }

    public Variable getParameter(SootMethod sm, ParameterRef ref) {
        return getParameter(sm, ref.getIndex());
    }

    public Method getMethod(SootMethod method) {
        return getMethod(method.getSignature());
    }

    public Application getApplication() {
        return application;
    }

    public VariableManager getVariableManager() {
        return variableManager;
    }
   
    public void setExpressionVariable(Value value, Variable variable) {
        expression_map.put(value, variable);
    }
   
    public Variable getExpressionVariable(Value value) {
        return expression_map.get(value);
    }

    public Automaton getFieldType(SootField field) {
        return staticStringTypes.getFieldType(field);
    }

    public Automaton getMethodReturnType(SootMethod method) {
        return staticStringTypes.getMethodReturnType(method);
    }

    public Automaton getParameterType(SootMethod method, int paramIndex) {
        return staticStringTypes.getParameterType(method, paramIndex);
    }
   
    public boolean isSubtypeOf(SootClass a, SootClass b) {
      if (a.equals(b))
        return true;
      if (b.getType().equals(RefType.v("java.lang.Object")))
        return true;
      if (a.isInterface()) {
        return b.isInterface() && class_hierarchy.isInterfaceSubinterfaceOf(a, b);
      }
      if (b.isInterface()) {
        return class_hierarchy.getImplementersOf(b).contains(a);
      }
      return class_hierarchy.isClassSubclassOf(a, b);
    }
   
    public Automaton resolveToStringMethod(SootClass a) {
      for (Resolver resolver : resolvers) {
        Automaton result = resolver.resolveToString(a);
        if (result != null)
          return result;
      }
      return null;
    }
   
    /**
     * Given the static type of a variable, returns the most specific variable type
     * we can assign to it.
     * <p/>
     * For example, a variable of type <tt>Cloneable</tt> might be an array, so this returns
     * <tt>ARRAY</tt> if the argument equals <tt>java.lang.Cloneable</tt>. On the other hand, if
     * the argument is <tt>java.io.Serializable</tt>, it returns <tt>OBJECT</tt> because all
     * the interesting types implement <tt>Serializable</tt>.
     * @param type a soot type
     * @return a variable type. <tt>null</tt> and {@link VariableType#NULL} are never returned.
     */
    public VariableType fromSootType(Type type) {
        if (type instanceof RefType) {
          SootClass clazz = ((RefType)type).getSootClass();
          if (isSubtypeOf(clazz, Scene.v().getSootClass("java.util.Collection"))
              || isSubtypeOf(clazz, Scene.v().getSootClass("java.util.Iterator"))) {
            return VariableType.ARRAY;
          }
          // otherwise, look in the table
            String className = ((RefType)type).getClassName();
            return fromCanonicalName(className);
        } else if (type instanceof ArrayType) {
            // TODO because of covariant arrays, we currently assume String[].
            // find out exactly which array types might be a covariant of String[]. (for example int[] cannot be)
            // XXX now that primitive arrays are also supported, char[], etc. is actually allowed
            return VariableType.ARRAY; 
        } else if (type instanceof NullType) {
            return VariableType.NULL;
        } else if (type instanceof PrimType) {
          // FIXME ignore int,long,float,double here? only 16-bit primitives are suported anyway
            return VariableType.PRIMITIVE;
        }
        return VariableType.NONE;
    }
   
    /**
     * Given the static type of a variable, returns the most specific variable type
     * we can assign to it.
     * <p/>
     * For example, a variable of type <tt>Cloneable</tt> might be an array, so this returns
     * <tt>ARRAY</tt> if the argument equals <tt>java.lang.Cloneable</tt>. On the other hand, if
     * the argument is <tt>java.lang.Serializable</tt>, it returns <tt>OBJECT</tt> because all
     * the interesting types implement <tt>Serializable</tt>.
     * @param canonicalName fully qualified name of a Java class.
     * @return a variable type. <tt>NONE</tt> is (rightfully) returned for unknown types. <tt>null</tt> is never returned.
     */
    private static VariableType fromCanonicalName(String canonicalName) {
        VariableType type = javaTypesToVariableTypes.get(canonicalName);
        if (type == null)
            return VariableType.NONE;
        return type;
    }
   
    private static final HashMap<String, VariableType> javaTypesToVariableTypes;
    static {
        javaTypesToVariableTypes = new HashMap<String, VariableType>();
        javaTypesToVariableTypes.put("java.lang.Object", VariableType.OBJECT);
        javaTypesToVariableTypes.put("java.io.Serializable", VariableType.OBJECT);
        javaTypesToVariableTypes.put("java.lang.String", VariableType.STRING);
        javaTypesToVariableTypes.put("java.lang.StringBuffer", VariableType.STRINGBUFFER);
        javaTypesToVariableTypes.put("java.lang.StringBuilder", VariableType.STRINGBUFFER);
        javaTypesToVariableTypes.put("java.lang.Appendable", VariableType.STRINGBUFFER);
        javaTypesToVariableTypes.put("java.lang.Cloneable", VariableType.ARRAY);
        javaTypesToVariableTypes.put("java.lang.CharSequence", VariableType.OBJECT);
        //javaTypesToVariableTypes.put("java.lang.Comparable", STRING);     // String implements Comparable<String>
       
        javaTypesToVariableTypes.put("java.lang.AbstractStringBuilder", VariableType.STRINGBUFFER);
       
        // collections
        javaTypesToVariableTypes.put("java.util.Collection", VariableType.ARRAY);
        javaTypesToVariableTypes.put("java.util.List", VariableType.ARRAY);
        javaTypesToVariableTypes.put("java.util.ArrayList", VariableType.ARRAY);
        javaTypesToVariableTypes.put("java.util.Vector", VariableType.ARRAY);
        javaTypesToVariableTypes.put("java.util.LinkedList", VariableType.ARRAY);
        javaTypesToVariableTypes.put("java.util.Set", VariableType.ARRAY);
        javaTypesToVariableTypes.put("java.util.HashSet", VariableType.ARRAY);
        javaTypesToVariableTypes.put("java.util.LinkedHashSet", VariableType.ARRAY);
        javaTypesToVariableTypes.put("java.util.TreeSet", VariableType.ARRAY);
       
        // iterators
        javaTypesToVariableTypes.put("java.util.Iterator", VariableType.ARRAY);
        javaTypesToVariableTypes.put("java.util.ListIterator", VariableType.ARRAY);

        // AbstractStringBuilder is a package-private class in the Java API that
        // Soot may decide to use as variable type. Both StringBuffer and StringBuilder extend it.
       
        // Comparable has been commented out because Strings are immutable, so we
        // don't care which strings might be aliases
    }
   
}
TOP

Related Classes of dk.brics.string.java.Jimple2Intermediate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.