All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sootup.java.sourcecode.frontend.WalaIRToJimpleConverter Maven / Gradle / Ivy

There is a newer version: 1.3.0
Show newest version
package sootup.java.sourcecode.frontend;
/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 2018-2020 Linghui Luo, Christian Brüggemann, Markus Schmidt
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
import com.ibm.wala.cast.loader.AstClass;
import com.ibm.wala.cast.loader.AstField;
import com.ibm.wala.cast.loader.AstMethod;
import com.ibm.wala.cast.loader.AstMethod.DebuggingInformation;
import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
import com.ibm.wala.cfg.AbstractCFG;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.shrike.shrikeCT.ClassConstants;
import com.ibm.wala.shrike.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.*;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashSetFactory;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import javax.annotation.Nonnull;
import sootup.core.frontend.OverridingBodySource;
import sootup.core.frontend.OverridingClassSource;
import sootup.core.graph.MutableBlockStmtGraph;
import sootup.core.inputlocation.AnalysisInputLocation;
import sootup.core.jimple.Jimple;
import sootup.core.jimple.basic.*;
import sootup.core.jimple.common.stmt.BranchingStmt;
import sootup.core.jimple.common.stmt.JReturnVoidStmt;
import sootup.core.jimple.common.stmt.JThrowStmt;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.*;
import sootup.core.signatures.FieldSignature;
import sootup.core.signatures.MethodSignature;
import sootup.core.types.ClassType;
import sootup.core.types.NullType;
import sootup.core.types.PrimitiveType;
import sootup.core.types.Type;
import sootup.core.types.VoidType;
import sootup.java.core.JavaIdentifierFactory;
import sootup.java.core.JavaSootClass;
import sootup.java.core.JavaSootClassSource;
import sootup.java.core.OverridingJavaClassSource;
import sootup.java.core.types.AnnotationType;
import sootup.java.core.types.JavaClassType;
import sootup.java.sourcecode.inputlocation.JavaSourcePathAnalysisInputLocation;

/**
 * Converter which converts WALA IR to jimple.
 *
 * @author Linghui Luo
 */
public class WalaIRToJimpleConverter {

  final JavaIdentifierFactory identifierFactory;
  private final AnalysisInputLocation srcNamespace;
  private final HashMap clsWithInnerCls;
  private final HashMap walaToSootNameTable;
  private Set sootFields;

  public WalaIRToJimpleConverter(@Nonnull Set sourceDirPath) {
    srcNamespace = new JavaSourcePathAnalysisInputLocation(sourceDirPath);
    // TODO: [ms] get identifierFactory from view - view can hold a different implementation
    identifierFactory = JavaIdentifierFactory.getInstance();
    clsWithInnerCls = new HashMap<>();
    walaToSootNameTable = new HashMap<>();
  }

  public WalaIRToJimpleConverter(@Nonnull Set sourceDirPath, @Nonnull SourceType srcType) {
    srcNamespace = new JavaSourcePathAnalysisInputLocation(srcType, sourceDirPath);
    // TODO: [ms] get identifierFactory from view - view can hold a different implementation
    identifierFactory = JavaIdentifierFactory.getInstance();
    clsWithInnerCls = new HashMap<>();
    walaToSootNameTable = new HashMap<>();
  }

  /**
   * Convert a wala {@link AstClass} to {@link SootClass}.
   *
   * @return A SootClass converted from walaClass
   */
  // TODO: remove deprecated
  @Deprecated
  public SootClass convertClass(AstClass walaClass) {
    JavaSootClassSource classSource = convertToClassSource(walaClass);
    // TODO: [ms] fix fixed SourceType - get it from project
    return new JavaSootClass(classSource, SourceType.Application);
  }

  JavaSootClassSource convertToClassSource(AstClass walaClass) {
    String fullyQualifiedClassName = convertClassNameFromWala(walaClass.getName().toString());
    JavaClassType classSig = identifierFactory.getClassType(fullyQualifiedClassName);
    // get super class
    IClass sc = walaClass.getSuperclass();
    JavaClassType superClass = null;
    if (sc != null) {
      superClass =
          identifierFactory.getClassType(convertClassNameFromWala(sc.getName().toString()));
    }

    // get interfaces
    Set interfaces = new HashSet<>();
    for (IClass i : walaClass.getDirectInterfaces()) {
      JavaClassType inter =
          identifierFactory.getClassType(convertClassNameFromWala(i.getName().toString()));
      interfaces.add(inter);
    }

    // get outer class
    JavaClassType outerClass = null;
    if (walaClass instanceof com.ibm.wala.cast.java.loader.JavaSourceLoaderImpl.JavaClass) {
      com.ibm.wala.cast.java.loader.JavaSourceLoaderImpl.JavaClass javaClass =
          (com.ibm.wala.cast.java.loader.JavaSourceLoaderImpl.JavaClass) walaClass;
      IClass ec = javaClass.getEnclosingClass();
      if (ec != null) {
        outerClass =
            identifierFactory.getClassType(convertClassNameFromWala(ec.getName().toString()));
      }
    }

    // add source position
    Position position = walaClass.getSourcePosition();

    // convert modifiers
    EnumSet modifiers = convertModifiers(walaClass);

    // convert fields
    Set fields = HashSetFactory.make(walaClass.getDeclaredInstanceFields());
    fields.addAll(walaClass.getDeclaredStaticFields());
    sootFields = new HashSet<>();
    for (IField walaField : fields) {
      SootField sootField = convertField(classSig, (AstField) walaField);
      sootFields.add(sootField);
    }

    if (outerClass != null) {
      // create enclosing reference to outerClass
      FieldSignature signature =
          identifierFactory.getFieldSignature("this$0", classSig, outerClass);
      SootField enclosingObject =
          new SootField(signature, EnumSet.of(Modifier.FINAL), NoPositionInformation.getInstance());
      sootFields.add(enclosingObject);
    }

    // convert methods
    Set sootMethods = new HashSet<>();

    for (IMethod walaMethod : walaClass.getDeclaredMethods()) {
      SootMethod sootMethod = convertMethod(classSig, (AstMethod) walaMethod);
      sootMethods.add(sootMethod);
    }

    return createClassSource(
        walaClass,
        superClass,
        interfaces,
        outerClass,
        sootFields,
        sootMethods,
        position,
        modifiers,
        Collections.emptyList() // TODO:[ms] implement annotations);
        );
  }

  /** Create a {@link OverridingClassSource} object for the given walaClass. */
  public OverridingJavaClassSource createClassSource(
      AstClass walaClass,
      JavaClassType superClass,
      Set interfaces,
      JavaClassType outerClass,
      Set sootFields,
      Set sootMethods,
      Position position,
      EnumSet modifiers,
      Iterable annotations) {
    String fullyQualifiedClassName = convertClassNameFromWala(walaClass.getName().toString());
    JavaClassType classSignature = identifierFactory.getClassType(fullyQualifiedClassName);
    URL url = walaClass.getSourceURL();
    Path sourcePath = Paths.get(url.getPath());
    return new OverridingJavaClassSource(
        srcNamespace,
        sourcePath,
        classSignature,
        superClass,
        interfaces,
        outerClass,
        sootFields,
        sootMethods,
        convertPosition(position),
        modifiers,
        Collections.emptyList(), // TODO:[ms] implement annotations
        Collections.emptyList(), // TODO:[ms] implement methodannotations
        Collections.emptyList()); // TODO:[ms] implement fieldannotations
  }

  /**
   * Convert a wala {@link AstField} to {@link SootField}.
   *
   * @param classSig the class owns the field
   * @param walaField the wala field
   * @return A SootField object converted from walaField.
   */
  public SootField convertField(JavaClassType classSig, AstField walaField) {
    Type type = convertType(walaField.getFieldTypeReference());
    EnumSet modifiers = convertModifiers(walaField);
    FieldSignature signature =
        identifierFactory.getFieldSignature(walaField.getName().toString(), classSig, type);
    return new SootField(signature, modifiers, NoPositionInformation.getInstance());
  }

  /**
   * Convert a wala {@link AstMethod} to {@link SootMethod} and add it into the given sootClass.
   *
   * @param classSig the SootClass which should contain the converted SootMethod
   * @param walaMethod the walMethod to be converted
   */
  public SootMethod convertMethod(JavaClassType classSig, AstMethod walaMethod) {
    // create SootMethod instance
    List paraTypes = new ArrayList<>();
    List sigs = new ArrayList<>();
    if (walaMethod.symbolTable() != null) {

      for (int i = walaMethod.isStatic() ? 0 : 1; i < walaMethod.getNumberOfParameters(); i++) {
        TypeReference type = walaMethod.getParameterType(i);
        Type paraType = convertType(type);
        paraTypes.add(identifierFactory.getType(paraType.toString()));
        sigs.add(paraType.toString());
      }
    }

    Type returnType = convertType(walaMethod.getReturnType());

    EnumSet modifiers = convertModifiers(walaMethod);

    List thrownExceptions = new ArrayList<>();
    try {
      for (TypeReference exception : walaMethod.getDeclaredExceptions()) {
        String exceptionName = convertClassNameFromWala(exception.getName().toString());
        JavaClassType exceptionSig = identifierFactory.getClassType(exceptionName);
        thrownExceptions.add(exceptionSig);
      }
    } catch (UnsupportedOperationException | InvalidClassFileException e) {
      e.printStackTrace();
    }
    // add debug info
    DebuggingInformation debugInfo = walaMethod.debugInfo();
    MethodSignature methodSig =
        identifierFactory.getMethodSignature(
            classSig, walaMethod.getName().toString(), returnType.toString(), sigs);

    Body body = createBody(methodSig, modifiers, walaMethod);
    return new WalaSootMethod(
        new OverridingBodySource(methodSig, body),
        methodSig,
        modifiers,
        thrownExceptions,
        debugInfo);
  }

  public Type convertType(TypeReference type) {
    if (type.isPrimitiveType()) {
      if (type.equals(TypeReference.Boolean)) {
        return PrimitiveType.getBoolean();
      } else if (type.equals(TypeReference.Byte)) {
        return PrimitiveType.getByte();
      } else if (type.equals(TypeReference.Char)) {
        return PrimitiveType.getChar();
      } else if (type.equals(TypeReference.Short)) {
        return PrimitiveType.getShort();
      } else if (type.equals(TypeReference.Int)) {
        return PrimitiveType.getInt();
      } else if (type.equals(TypeReference.Long)) {
        return PrimitiveType.getLong();
      } else if (type.equals(TypeReference.Float)) {
        return PrimitiveType.getFloat();
      } else if (type.equals(TypeReference.Double)) {
        return PrimitiveType.getDouble();
      } else if (type.equals(TypeReference.Void)) {
        return VoidType.getInstance();
      }
    } else if (type.isReferenceType()) {
      if (type.isArrayType()) {
        TypeReference t = type.getInnermostElementType();
        Type baseType = convertType(t);
        int dim = type.getDimensionality();
        return identifierFactory.getArrayType(baseType, dim);
      } else if (type.isClassType()) {
        if (type.equals(TypeReference.Null)) {
          return NullType.getInstance();
        } else {
          String className = convertClassNameFromWala(type.getName().toString());
          return identifierFactory.getClassType(className);
        }
      }
    }
    throw new RuntimeException("Unsupported type: " + type);
  }

  /** Return all modifiers for the given field. */
  public EnumSet convertModifiers(AstField field) {
    EnumSet modifiers = EnumSet.noneOf(Modifier.class);
    if (field.isFinal()) {
      modifiers.add(Modifier.FINAL);
    }
    if (field.isPrivate()) {
      modifiers.add(Modifier.PRIVATE);
    }
    if (field.isProtected()) {
      modifiers.add(Modifier.PROTECTED);
    }
    if (field.isPublic()) {
      modifiers.add(Modifier.PUBLIC);
    }
    if (field.isStatic()) {
      modifiers.add(Modifier.STATIC);
    }
    if (field.isVolatile()) {
      modifiers.add(Modifier.VOLATILE);
    }
    // TODO: TRANSIENT field
    return modifiers;
  }

  /** Return all modifiers for the given method. */
  public EnumSet convertModifiers(AstMethod method) {
    EnumSet modifiers = EnumSet.noneOf(Modifier.class);
    if (method.isPrivate()) {
      modifiers.add(Modifier.PRIVATE);
    }
    if (method.isProtected()) {
      modifiers.add(Modifier.PROTECTED);
    }
    if (method.isPublic()) {
      modifiers.add(Modifier.PUBLIC);
    }
    if (method.isStatic()) {
      modifiers.add(Modifier.STATIC);
    }
    if (method.isFinal()) {
      modifiers.add(Modifier.FINAL);
    }
    if (method.isAbstract()) {
      modifiers.add(Modifier.ABSTRACT);
    }
    if (method.isSynchronized()) {
      modifiers.add(Modifier.SYNCHRONIZED);
    }
    if (method.isNative()) {
      modifiers.add(Modifier.NATIVE);
    }
    if (method.isSynthetic()) {
      modifiers.add(Modifier.SYNTHETIC);
    }
    if (method.isBridge()) {
      modifiers.add(Modifier.VOLATILE);
    }
    // TODO: strictfp and annotation
    return modifiers;
  }

  public EnumSet convertModifiers(AstClass klass) {
    int modif = klass.getModifiers();
    EnumSet modifiers = EnumSet.noneOf(Modifier.class);
    if (klass.isAbstract()) {
      modifiers.add(Modifier.ABSTRACT);
    }
    if (klass.isPrivate()) {
      modifiers.add(Modifier.PRIVATE);
    }
    if (klass.isSynthetic()) {
      modifiers.add(Modifier.SYNTHETIC);
    }
    if (klass.isPublic()) {
      modifiers.add(Modifier.PUBLIC);
    }
    if (klass.isInterface()) {
      modifiers.add(Modifier.INTERFACE);
    }
    if (klass.getSuperclass().getName().toString().equals("Ljava/lang/Enum")) {
      modifiers.add(Modifier.ENUM);
    }
    if ((modif & ClassConstants.ACC_FINAL) != 0) modifiers.add(Modifier.FINAL);
    // TODO:  annotation
    return modifiers;
  }

  @Nonnull
  private Body createBody(
      MethodSignature methodSignature, EnumSet modifiers, AstMethod walaMethod) {

    if (walaMethod.isAbstract()) {
      return Body.builder().setMethodSignature(methodSignature).build();
    }

    List traps = new ArrayList<>();
    List stmtList = new ArrayList<>();

    AbstractCFG cfg = walaMethod.cfg();
    if (cfg != null) {
      LocalGenerator localGenerator = new LocalGenerator(new HashSet<>());
      // convert all wala instructions to jimple statements
      SSAInstruction[] insts = (SSAInstruction[]) cfg.getInstructions();
      if (insts.length > 0) {

        // set position for body
        DebuggingInformation debugInfo = walaMethod.debugInfo();
        Position bodyPos = debugInfo.getCodeBodyPosition();

        /* Look AsmMethodSourceContent.getBody, see AsmMethodSourceContent.emitLocals(); */

        if (!Modifier.isStatic(modifiers)) {
          JavaClassType thisType = (JavaClassType) methodSignature.getDeclClassType();
          Local thisLocal = localGenerator.generateThisLocal(thisType);
          Stmt stmt =
              Jimple.newIdentityStmt(
                  thisLocal,
                  Jimple.newThisRef(thisType),
                  convertPositionInfo(debugInfo.getInstructionPosition(0), null));
          stmtList.add(stmt);
        }

        // wala's first parameter is the "this" reference for non-static methods
        if (walaMethod.isStatic()) {
          for (int i = 0; i < walaMethod.getNumberOfParameters(); i++) {
            Type type = convertType(walaMethod.getParameterType(i));
            Local paraLocal = localGenerator.generateParameterLocal(type, i);

            Stmt stmt =
                Jimple.newIdentityStmt(
                    paraLocal,
                    Jimple.newParameterRef(type, i),
                    convertPositionInfo(debugInfo.getInstructionPosition(0), null));
            stmtList.add(stmt);
          }
        } else {
          for (int i = 1; i < walaMethod.getNumberOfParameters(); i++) {
            Type type = convertType(walaMethod.getParameterType(i));
            Local paraLocal = localGenerator.generateParameterLocal(type, i);

            Stmt stmt =
                Jimple.newIdentityStmt(
                    paraLocal,
                    Jimple.newParameterRef(type, i - 1),
                    convertPositionInfo(debugInfo.getInstructionPosition(0), null));
            stmtList.add(stmt);
          }
        }

        InstructionConverter instConverter =
            new InstructionConverter(this, methodSignature, walaMethod, localGenerator);
        HashMap index2Stmt = new HashMap<>();
        Stmt stmt = null;
        boolean isVoidMethod = walaMethod.getReturnType().equals(TypeReference.Void);

        for (SSAInstruction inst : insts) {

          // WALA sets target of goto to -1 if the goto points to a dead block
          if (!isVoidMethod && inst instanceof SSAGotoInstruction) {
            if (((SSAGotoInstruction) inst).getTarget() == -1) {
              continue;
            }
          }

          List retStmts = instConverter.convertInstruction(inst, index2Stmt);
          if (!retStmts.isEmpty()) {
            final int retStmtsSize = retStmts.size();
            stmt = retStmts.get(0);
            stmtList.add(stmt);
            index2Stmt.put(inst.iIndex(), stmt);

            for (int i = 1; i < retStmtsSize; i++) {
              stmt = retStmts.get(i);
              stmtList.add(stmt);
            }
          }
        }

        // add return void stmt for methods with return type being void
        if (isVoidMethod) {
          Stmt ret;
          final boolean isImplicitLastStmtTargetOfBranchStmt = instConverter.hasJumpTarget(-1);
          final boolean validMethodLeaving =
              !(stmt instanceof JReturnVoidStmt || stmt instanceof JThrowStmt);
          if (index2Stmt.isEmpty() || validMethodLeaving || isImplicitLastStmtTargetOfBranchStmt) {
            // TODO? [ms] InstructionPosition of last line in the method seems strange to me ->
            // maybe use lastLine with
            // startcol: -1 because it does not exist in the source explicitly?
            ret =
                Jimple.newReturnVoidStmt(
                    convertPositionInfo(debugInfo.getInstructionPosition(insts.length - 1), null));
            stmtList.add(ret);
          } else {
            ret = stmt;
          }
          // needed because referencing a branch to the last stmt refers to: -1
          index2Stmt.put(-1, ret);
        }

        final Map> branchingMap = instConverter.setUpTargets(index2Stmt);

        // calculate trap information
        for (Map.Entry, TypeReference[]> catchBlockEntry :
            walaMethod.catchTypes().entrySet()) {

          final IBasicBlock block = catchBlockEntry.getKey();
          final TypeReference[] exceptionTypes = catchBlockEntry.getValue();

          /*          final Collection> exceptionalPredecessors = cfg.getExceptionalPredecessors(block);

                    for (IBasicBlock exceptionalPredecessor : exceptionalPredecessors) {
                      Stmt from = index2Stmt.get(exceptionalPredecessor.getFirstInstructionIndex());
                      Stmt to = index2Stmt.get(exceptionalPredecessor.getLastInstructionIndex() + 1); // exclusive!

                      Stmt handlerStmt = index2Stmt.get(block.getFirstInstructionIndex());
                      for (TypeReference type : exceptionTypes) {
                        ClassType exception = (ClassType) convertType(type);
                        traps.add(new Trap(exception, from, to, handlerStmt));
                      }
                    }
          */
        }

        MutableBlockStmtGraph graph = new MutableBlockStmtGraph();
        graph.initializeWith(stmtList, branchingMap, traps);

        return Body.builder(graph)
            .setMethodSignature(methodSignature)
            .setLocals(localGenerator.getLocals())
            .setPosition(convertPosition(bodyPos))
            .build();
      }
    }

    throw new IllegalStateException("can not create Body - no CFG from WALA present.");
  }

  /**
   * Convert className in wala-format to soot-format, e.g., wala-format: Ljava/lang/String ->
   * soot-format: java.lang.String.
   *
   * @param className in wala-format
   * @return className in sootup.format
   */
  public String convertClassNameFromWala(String className) {
    String cl = className.intern();
    final String sootName = walaToSootNameTable.get(cl);
    if (sootName != null) {
      return sootName;
    }
    StringBuilder sb = new StringBuilder();
    if (className.startsWith("L")) {
      className = className.substring(1);
      String[] subNames = className.split("/");
      boolean isSpecial = false;
      for (String subName : subNames) {
        if (subName.contains("(") || subName.contains("<")) {
          // handle anonymous or inner classes
          isSpecial = true;
          break;
        }
        sb.append(subName).append('.');
      }
      if (subNames.length != 0) {
        sb.setLength(sb.length() - 1);
      }

      if (isSpecial) {
        String lastSubName = subNames[subNames.length - 1];
        String[] temp = lastSubName.split(">");
        if (temp.length > 0) {
          String name = temp[temp.length - 1];
          if (!name.contains("$")) {
            // This is an inner class
            String outClass = sb.toString();
            int count;
            final Integer innerClassCount = clsWithInnerCls.get(outClass);
            if (innerClassCount != null) {
              count = innerClassCount + 1;
            } else {
              count = 1;
            }
            clsWithInnerCls.put(outClass, count);
            sb.append(count).append("$");
          }
          sb.append(name);
        }
      }
    } else {
      throw new RuntimeException("Can not convert WALA class name: " + className);
    }
    String ret = sb.toString();
    walaToSootNameTable.put(cl, ret);
    return ret;
  }

  /**
   * Convert className in soot-format to wala-format, e.g.,soot-format: java.lang.String.->
   * wala-format: Ljava/lang/String
   */
  public String convertClassNameFromSoot(String signature) {
    return "L" + signature.replace('.', '/');
  }

  protected void addSootField(SootField field) {
    if (this.sootFields != null) {
      this.sootFields.add(field);
    }
  }

  /*
   *   converts Wala Position to Soots Position
   * */
  public static sootup.core.model.Position convertPosition(Position instructionPosition) {
    return new FullPosition(
        instructionPosition.getFirstLine(),
        instructionPosition.getFirstCol(),
        instructionPosition.getLastLine(),
        instructionPosition.getLastCol());
  }

  public static StmtPositionInfo convertPositionInfo(
      Position instructionPosition, Position[] operandPosition) {

    if (operandPosition == null) {
      return new FullStmtPositionInfo(convertPosition(instructionPosition), null);
    }
    FullPosition[] operandPos =
        Arrays.stream(operandPosition)
            .map(
                instrPos ->
                    instrPos == null
                        ? null
                        : new FullPosition(
                            instrPos.getFirstLine(),
                            instrPos.getFirstCol(),
                            instrPos.getLastLine(),
                            instrPos.getLastCol()))
            .toArray(sootup.core.model.FullPosition[]::new);

    return new FullStmtPositionInfo(convertPosition(instructionPosition), operandPos);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy