code: /**
   * This file is part of Eclipse Steady.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   * SPDX-License-Identifier: Apache-2.0
   * SPDX-FileCopyrightText: Copyright (c) 2018-2020 SAP SE or an SAP affiliate company and Eclipse Steady contributors
   */
  package org.eclipse.steady.cg.soot;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import soot.*;
  import soot.javaToJimple.LocalGenerator;
  import soot.jimple.*;
  import soot.jimple.infoflow.data.SootMethodAndClass;
  import soot.jimple.infoflow.entryPointCreators.DefaultEntryPointCreator;
  import soot.jimple.infoflow.util.SootMethodRepresentationParser;
  import soot.jimple.internal.JNopStmt;
  
  import java.util.*;
  
  /**
   * <p>CustomEntryPointCreator class.</p>
   */
  public class CustomEntryPointCreator extends DefaultEntryPointCreator {
  
    private static final Logger logger = LoggerFactory.getLogger(CustomEntryPointCreator.class);
  
    private final Collection<SootClass> dummyClasses = new HashSet<>();
  
    /**
     * <p>Constructor for CustomEntryPointCreator.</p>
     *
     * @param methodsToCall a {@link java.util.Collection} object.
     */
    public CustomEntryPointCreator(Collection<String> methodsToCall) {
  
      super(methodsToCall);
      generateAppropriateDummyClasses(methodsToCall);
    }
  
    /**
     * <p>generateAppropriateDummyClasses.</p>
     *
     * @param methodsToCall a {@link java.util.Collection} object.
     */
    public void generateAppropriateDummyClasses(Collection<String> methodsToCall) {
      final Map<String, Set<String>> classMap =
          SootMethodRepresentationParser.v().parseClassNames(methodsToCall, false);
      for (Map.Entry<String, Set<String>> e : classMap.entrySet()) {
        SootClass createdClass = Scene.v().getSootClass(e.getKey());
        if (createdClass.isConcrete()
            && !createdClass.isPhantom()
            && !createdClass.isPhantomClass()) {
          for (String method : e.getValue()) {
            SootMethodAndClass methodAndClass =
                SootMethodRepresentationParser.v().parseSootMethodString(method);
            SootMethod methodToInvoke =
                findMethod(
                    Scene.v().getSootClass(methodAndClass.getClassName()),
                    methodAndClass.getSubSignature());
  
            List<Type> parameterTypes = methodToInvoke.getParameterTypes();
            // check if we actually have concrete parameters for these classes, otherwise generate
            // dummyclasses
            for (Type parameterType : parameterTypes) {
              if (super.isSimpleType(parameterType.getEscapedName())) {
                continue;
              }
              if (!(parameterType instanceof RefType)) {
                continue;
              }
              SootClass class2Search = ((RefType) parameterType).getSootClass();
              // check if a concrete subclass exists
              boolean compatibleTypeExists = concreteSubClassExists(class2Search);
  
              if (!compatibleTypeExists) {
  
                if (Scene.v().isExcluded(class2Search)) {
                  SootClass dummyClass = getDummyClass(class2Search);
                  this.dummyClasses.add(dummyClass);
                }
              }
            }
          }
        }
      }
    }
  
    /**
     * <p>concreteSubClassExists.</p>
     *
     * @param classToType a {@link soot.SootClass} object.
     * @return a boolean.
     */
    public boolean concreteSubClassExists(SootClass classToType) {
      if (classToType.isAbstract() || classToType.isInterface()) {
        // check if a public exported class exists implementing this interface or extending the
        // abstract class
        if (classToType.isInterface()) {
  
          for (SootClass implementorOfSootClass :
              Scene.v().getActiveHierarchy().getImplementersOf(classToType)) {
            if (!implementorOfSootClass.isAbstract()) return true;
          }
  
        } else {
          for (SootClass sootSubClass : Scene.v().getActiveHierarchy().getSubclassesOf(classToType)) {
            if (isCompatible(sootSubClass, classToType) && !sootSubClass.isAbstract()) return true;
          }
        }
  
        return false;
      }
      return classToType.isConcrete();
    }
  
    /**
     * <p>Getter for the field <code>dummyClasses</code>.</p>
     *
     * @return a {@link java.util.Collection} object.
     */
    public Collection getDummyClasses() {
      return this.dummyClasses;
    }
  
    /**
     * <p>getDummyClass.</p>
     *
     * @param toImplement a {@link soot.SootClass} object.
     * @return a {@link soot.SootClass} object.
     */
    public SootClass getDummyClass(SootClass toImplement) {
      String packageName = toImplement.getJavaPackageName();
  
      String clzName = toImplement.getJavaStyleName();
  
      String dummyClassName = packageName + ".Dummy" + clzName;
      if (Scene.v().containsClass(dummyClassName)) return Scene.v().getSootClass(dummyClassName);
  
      SootClass dummyClass = new SootClass(dummyClassName);
      // dummyClass.setModifiers(toImplement.getModifiers() ^ Modifier.ABSTRACT);
      dummyClass.setModifiers(Modifier.PUBLIC);
  
      // create the constructor
      SootMethod constructor = new SootMethod("<init>", Collections.<Type>emptyList(), VoidType.v());
      dummyClass.addMethod(constructor);
      JimpleBody body = Jimple.v().newBody(constructor);
  
      // Add this reference
      body.insertIdentityStmts();
      // special invoke Object Init
      //
      SootClass objectClazz = Scene.v().getSootClass("java.lang.Object");
      SootMethodRef methodRef;
      if (objectClazz.declaresMethod("void <init>()")) {
        SootMethod method = objectClazz.getMethod("void <init>()");
        methodRef = method.makeRef();
      } else {
        methodRef = Scene.v().makeConstructorRef(objectClazz, Collections.<Type>emptyList());
      }
      SpecialInvokeExpr expr = Jimple.v().newSpecialInvokeExpr(body.getThisLocal(), methodRef);
      Stmt invokeStmt = Jimple.v().newInvokeStmt(expr);
      body.getUnits().add(invokeStmt);
      Stmt ret = Jimple.v().newReturnStmt(body.getThisLocal());
      body.getUnits().add(ret);
  
      constructor.setActiveBody(body);
  
      /* handle multiple interfaces and cases in which  an interface extends another interface         */
      if (toImplement.isAbstract()) {
  
        // if class is an interface it might extend another interface, then we have to implement
        // the superclass methods as well
  
        // if class is abstract
        // a) it might implements several interfaces, that needs to be implemented
  
        // b) extends a superclass which is also abstract
        // b.2) extends a superclass whose superclass is also abstract (so on...)
        // c) these superclasses might implements several interfaces
  
        HashSet<SootMethod> methodsToImplement = new HashSet<>();
  
        HashSet<SootClass> classesWhoseMethodsMustBeImplemented = new HashSet<>();
        SootClass classToVisit = toImplement;
  
        while (classToVisit.isAbstract()) {
          classesWhoseMethodsMustBeImplemented.add(classToVisit);
          classToVisit = classToVisit.getSuperclass();
        }
  
        for (SootClass classWhichMethodsMustBeImplemented : classesWhoseMethodsMustBeImplemented) {
          methodsToImplement.addAll(classWhichMethodsMustBeImplemented.getMethods());
          for (SootClass interfaceToImplement : classWhichMethodsMustBeImplemented.getInterfaces()) {
            methodsToImplement.addAll(interfaceToImplement.getMethods());
          }
        }
        // above we might added methods which are already implemented but we catch this latter in the
        // for loop for actually generating the methods
  
        if (toImplement.isInterface()) {
          dummyClass.addInterface(toImplement);
          dummyClass.setSuperclass(Scene.v().getSootClass("java.lang.Object"));
  
        } else {
          // we have an abstract class
          dummyClass.setSuperclass(toImplement);
        }
        for (SootMethod parentMethod : methodsToImplement) {
  
          if (parentMethod
              .isAbstract()) { // if we have added to much methods above, we only generate methods for
            // the abstract ones here
            // the next if statement deals with name clashes of methods of several interfaces
            if (dummyClass.declaresMethod(
                parentMethod.getName(),
                parentMethod.getParameterTypes(),
                parentMethod.getReturnType())) {
              // a corresponding method  is already contained in the dummyClass; thus we don't need to
              // generate another one
              continue;
            }
            SootMethod generatedMethod = generateMethodImplementation(parentMethod, dummyClass);
            dummyClass.addMethod(generatedMethod);
          }
        }
      }
  
      // First add class to scene, then make it an application class
      // as addClass contains a call to "setLibraryClass"
      Scene.v().addClass(dummyClass);
      dummyClass.setApplicationClass();
      // add these classes to the dummyClass set to get the Parameter passed in at callbacks
      this.dummyClasses.add(dummyClass);
      return dummyClass;
    }
  
    private SootMethod generateMethodImplementation(
        SootMethod methodToImplement, final SootClass generatedDummyClass) {
      SootMethod generatedMethod =
          new SootMethod(
              methodToImplement.getName(),
              methodToImplement.getParameterTypes(),
              methodToImplement.getReturnType());
      Body body = Jimple.v().newBody();
      body.setMethod(generatedMethod);
      generatedMethod.setActiveBody(body);
  
      // add locals for Parameter
      // Add a parameter reference to the body
      LocalGenerator lg = new LocalGenerator(body);
  
      // create a local for the this reference
      if (!methodToImplement.isStatic()) {
        Local thisLocal = lg.generateLocal(generatedDummyClass.getType());
        body.getUnits()
            .addFirst(
                Jimple.v()
                    .newIdentityStmt(
                        thisLocal, Jimple.v().newThisRef(generatedDummyClass.getType())));
      }
  
      int i = 0;
      for (Type type : generatedMethod.getParameterTypes()) {
        Local paramLocal = lg.generateLocal(type);
        body.getUnits()
            .add(Jimple.v().newIdentityStmt(paramLocal, Jimple.v().newParameterRef(type, i)));
        i++;
      }
  
      JNopStmt startStmt = new JNopStmt();
      // JNopStmt endStmt = new JNopStmt();
  
      body.getUnits().add(startStmt);
  
      // check if return type is void (check first, since next call includes void)
      if (methodToImplement.getReturnType() instanceof VoidType) {
        body.getUnits().add(Jimple.v().newReturnVoidStmt());
      }
      // if sootClass is simpleClass
      else if (isSimpleType(methodToImplement.getReturnType().toString())) {
        Local varLocal = lg.generateLocal(getSimpleTypeFromType(methodToImplement.getReturnType()));
  
        AssignStmt aStmt =
            Jimple.v()
                .newAssignStmt(varLocal, getSimpleDefaultValue(methodToImplement.getReturnType()));
        body.getUnits().add(aStmt);
        body.getUnits().add(Jimple.v().newReturnStmt(varLocal));
      } else {
        body.getUnits().add(Jimple.v().newReturnStmt(NullConstant.v()));
      }
  
      // remove the abstract Modifier from the new implemented method
      generatedMethod.setModifiers(methodToImplement.getModifiers() ^ Modifier.ABSTRACT);
  
      return generatedMethod;
    }
  
    protected Type getSimpleTypeFromType(Type type) {
      if (type.toString().equals("java.lang.String")) {
        assert type instanceof RefType;
  
        return RefType.v(((RefType) type).getSootClass());
      } else if (type.toString().equals("void")) {
        return VoidType.v();
      } else if (type.toString().equals("char")) {
        return CharType.v();
      } else if (type.toString().equals("byte")) {
        return ByteType.v();
      } else if (type.toString().equals("short")) {
        return ShortType.v();
      } else if (type.toString().equals("int")) {
        return IntType.v();
      } else if (type.toString().equals("float")) {
        return FloatType.v();
      } else if (type.toString().equals("long")) {
        return LongType.v();
      } else if (type.toString().equals("double")) {
        return DoubleType.v();
      } else if (type.toString().equals("boolean")) {
        return BooleanType.v();
      } else {
        throw new RuntimeException("Unknown simple type: " + type);
      }
    }
  }
  