org.eclipse.steady.cg.soot.CustomEntryPointCreator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lang-java-reach-soot Show documentation
Show all versions of lang-java-reach-soot Show documentation
Soot call graph constructor service
The newest version!
/**
* 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.*;
/**
* CustomEntryPointCreator class.
*/
public class CustomEntryPointCreator extends DefaultEntryPointCreator {
private static final Logger logger = LoggerFactory.getLogger(CustomEntryPointCreator.class);
private final Collection dummyClasses = new HashSet<>();
/**
* Constructor for CustomEntryPointCreator.
*
* @param methodsToCall a {@link java.util.Collection} object.
*/
public CustomEntryPointCreator(Collection methodsToCall) {
super(methodsToCall);
generateAppropriateDummyClasses(methodsToCall);
}
/**
* generateAppropriateDummyClasses.
*
* @param methodsToCall a {@link java.util.Collection} object.
*/
public void generateAppropriateDummyClasses(Collection methodsToCall) {
final Map> classMap =
SootMethodRepresentationParser.v().parseClassNames(methodsToCall, false);
for (Map.Entry> 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 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);
}
}
}
}
}
}
}
/**
* concreteSubClassExists.
*
* @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();
}
/**
* Getter for the field dummyClasses
.
*
* @return a {@link java.util.Collection} object.
*/
public Collection getDummyClasses() {
return this.dummyClasses;
}
/**
* getDummyClass.
*
* @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("", Collections.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 ()")) {
SootMethod method = objectClazz.getMethod("void ()");
methodRef = method.makeRef();
} else {
methodRef = Scene.v().makeConstructorRef(objectClazz, Collections.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 methodsToImplement = new HashSet<>();
HashSet 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);
}
}
}