Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/migrating.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Migration Help

### Version 3.0.2
- Identifiers are hash-consed: the `IdentifierFactory` hands out the same instance for equal `ClassType`s, `MethodSignature`s, `FieldSignature`s, their sub signatures and `PackageName`s. They can therefore be compared with `==` and used as keys of an `IdentityHashMap`.
- As a consequence the constructors of these classes are not public anymore. Obtain them from the `IdentifierFactory` of the `View` that the identifiers are used with:
```java
ClassType classType = view.getIdentifierFactory().getClassType("example.HelloWorld");
```
- `JavaIdentifierFactory.getInstance()` and `JavaModuleIdentifierFactory.getInstance()` were removed - the factory belongs to a `View` now. Use `view.getIdentifierFactory()`; if there is no `View` at hand (e.g. while setting one up), instantiate it with `new JavaIdentifierFactory()`.
- `JavaModuleIdentifierFactory.getInstance(module)` was replaced by the instance method `forModule(module)`:
```java
JavaModuleIdentifierFactory factory =
(JavaModuleIdentifierFactory) moduleView.getIdentifierFactory();
JavaModuleIdentifierFactory factoryOfModule = factory.forModule("mymodule");
```

### Version 1.3.0
- The Typehierarchy API is now returning `Stream<ClassType>` instead of `Collection<ClassType>`. The simplest fix to have the same behaviour as before would be to collect the Stream on your own ( e.g. via `.collect(Collectors.toList())` ).
- Default BytecodeBodyinterceptors are enabled to improve Jimple. To mitigate that adapt the List of BodyInterceptors to your needs.
Expand Down
18 changes: 18 additions & 0 deletions docs/whatsnew.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ Singletons offer a single view of a single program version, which makes it impos
SootUp does not make use of singletons such the `Scene` class in the old Soot any more. It enables analyzing multiple programs simultaneously.


### Hash-Consed Identifiers

Identifiers such as `ClassType`, `MethodSignature`, `FieldSignature`, their sub signatures and `PackageName` are hash-consed:
for a given name the `IdentifierFactory` always returns the very same instance.
This keeps the memory footprint of an analysis low and allows comparing identifiers with `==` as well as using them in an `IdentityHashMap`.
Because this invariant only holds if every identifier is created in one place, their constructors are not public - identifiers are created via the `IdentifierFactory` that belongs to the `View`:

!!! example

```java
IdentifierFactory identifierFactory = view.getIdentifierFactory();

ClassType classType = identifierFactory.getClassType("example.HelloWorld");
MethodSignature methodSignature =
identifierFactory.getMethodSignature(
classType, "main", "void", Collections.singletonList("java.lang.String[]"));
```

### Immutable by Design

SootUp has been designed with the goal of immutability in mind.
Expand Down
19 changes: 16 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@
<maven-javadoc-plugin.version>3.12.0</maven-javadoc-plugin.version>
<maven-central-publishing-plugin.version>0.11.0</maven-central-publishing-plugin.version>
<maven-gpg-plugin.version>3.2.8</maven-gpg-plugin.version>
<!-- Test JVM knobs, overridable on machines with less memory than CI,
e.g. -Dsurefire.threadCount=1 -Dsurefire.maxHeap=3072M -->
<surefire.maxHeap>8192M</surefire.maxHeap>
<surefire.threadCount>4</surefire.threadCount>
<licence-check.failOnMissingHeader>true</licence-check.failOnMissingHeader>
<reactorRootsTarget>${basedir}/target</reactorRootsTarget>
<junit.version>6.1.3</junit.version>
Expand Down Expand Up @@ -313,10 +317,19 @@
<version>${maven-surefire-plugin.version}</version>
<configuration>
<!-- Sets the VM argument line used when unit tests are run. -->
<argLine>-Xmx8192M ${surefireArgLine}</argLine>
<argLine>-Xmx${surefire.maxHeap} ${surefireArgLine}</argLine>
<trimStackTrace>false</trimStackTrace>
<parallel>all</parallel>
<useUnlimitedThreads>true</useUnlimitedThreads>
<!-- Parallelise whole test classes only. "all" additionally ran the methods of
a single class concurrently, which requires every test class to be thread
safe within itself; combined with unlimited threads it started as many
threads as there were test classes, so the peak memory of a run was
bounded by nothing but the size of the suite. Both knobs are properties so
a machine with less memory than CI can dial them down without editing this
file, e.g. -Dsurefire.threadCount=1 -Dsurefire.maxHeap=3072M -->
<parallel>classes</parallel>
<useUnlimitedThreads>false</useUnlimitedThreads>
<threadCount>${surefire.threadCount}</threadCount>
<perCoreThreadCount>false</perCoreThreadCount>
</configuration>
</plugin>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@
import java.util.*;
import java.util.stream.Collectors;
import sootup.callgraph.CallGraph;
import sootup.core.IdentifierFactory;
import sootup.core.graph.BasicBlock;
import sootup.core.graph.ControlFlowGraph;
import sootup.core.jimple.common.expr.JNewExpr;
import sootup.core.jimple.common.stmt.JAssignStmt;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.SootMethod;
import sootup.core.signatures.MethodSignature;
import sootup.core.signatures.MethodSubSignature;
import sootup.core.types.VoidType;
import sootup.core.util.DotExporter;
import sootup.core.views.View;
Expand Down Expand Up @@ -141,11 +141,12 @@ public static void connectEdgesToSubClasses(
methodSignatureInSubClass.forEach(
subclassmethodSignature -> {
Optional<? extends SootMethod> method = view.getMethod(target);
IdentifierFactory identifierFactory = view.getIdentifierFactory();
MethodSignature initMethod =
new MethodSignature(
identifierFactory.getMethodSignature(
subclassmethodSignature.getDeclClassType(),
new MethodSubSignature(
"<init>", Collections.emptyList(), VoidType.getInstance()));
identifierFactory.getMethodSubSignature(
"<init>", VoidType.getInstance(), Collections.emptyList()));
if (method.isPresent()
&& !subclassmethodSignature.toString().equals(initMethod.toString())) {
if (method.get().hasBody()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ public static String[] addStatement(String[] statements, String statement) {
}

private MutableBlockControlFlowGraph createControlFlowGraph() {
JavaIdentifierFactory factory = JavaIdentifierFactory.getInstance();
JavaIdentifierFactory factory = new JavaIdentifierFactory();
JavaClassType intType = factory.getClassType("int");
final MutableBlockControlFlowGraph graph = new MutableBlockControlFlowGraph();
Local l3 = JavaJimple.newLocal("l3", intType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@
import org.jf.dexlib2.iface.value.EncodedValue;
import org.jspecify.annotations.NonNull;
import sootup.apk.frontend.main.AndroidVersionInfo;
import sootup.core.IdentifierFactory;
import sootup.core.types.*;
import sootup.core.views.View;
import sootup.java.core.AnnotationUsage;
import sootup.java.core.JavaIdentifierFactory;
import sootup.java.core.types.JavaClassType;

public class DexUtil {

private static AndroidVersionInfo androidVersionInfo;

public static Type toSootType(String typeDescriptor, int pos) {
public static Type toSootType(
String typeDescriptor, int pos, @NonNull IdentifierFactory identifierFactory) {
Type type = null;
char typeDesignator = typeDescriptor.charAt(pos);
switch (typeDesignator) {
Expand Down Expand Up @@ -70,13 +70,13 @@ public static Type toSootType(String typeDescriptor, int pos) {
if (isByteCodeClassName(typeDescriptor)) {
typeDescriptor = dottedClassName(typeDescriptor);
}
type = getClassTypeFromClassName(typeDescriptor);
type = getClassTypeFromClassName(typeDescriptor, identifierFactory);
break;
case 'V': // void
type = VoidType.getInstance();
break;
case '[': // array
Type sootType = toSootType(typeDescriptor, pos + 1);
Type sootType = toSootType(typeDescriptor, pos + 1, identifierFactory);
if (sootType != null) {
type = Type.createArrayType(sootType, 1);
}
Expand Down Expand Up @@ -135,15 +135,16 @@ public static boolean isByteCodeClassName(String className) {
&& ((className.indexOf('/') != -1 || className.indexOf('.') == -1)));
}

public static ClassType getClassTypeFromClassName(String name) {
public static ClassType getClassTypeFromClassName(
String name, @NonNull IdentifierFactory identifierFactory) {
if (name.startsWith("[")) {
name = "java.lang.Object";
} else if (isByteCodeClassName(name)) {
name = dottedClassName(name);
}
JavaClassType javaClassType;
ClassType javaClassType;
try {
javaClassType = JavaIdentifierFactory.getInstance().getClassType(name);
javaClassType = identifierFactory.getClassType(name);
} catch (Exception exception) {
System.out.println("Exception when substring with className " + name);
throw new RuntimeException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,10 @@ protected List<AnnotationUsage> convertAnnotation(Set<? extends Annotation> anno
for (Annotation annotation : annotations) {
for (AnnotationElement element : annotation.getElements()) {
String name = element.getName();
paramMap.put(name, convertAnnotationValue(element.getValue().getValueType()));
paramMap.put(
name,
convertAnnotationValue(
element.getValue().getValueType(), getView().getIdentifierFactory()));
}
ClassType at =
getView()
Expand All @@ -209,12 +212,14 @@ protected List<AnnotationUsage> convertAnnotation(Set<? extends Annotation> anno
return annotationUsage;
}

private static Object convertAnnotationValue(Object annotationValue) {
private static Object convertAnnotationValue(
Object annotationValue, IdentifierFactory identifierFactory) {
if (annotationValue instanceof EncodedValue) {
ClassConstant classConstant = JavaJimple.newClassConstant(annotationValue.toString());
return ConstantUtil.fromObject(classConstant);
ClassConstant classConstant =
JavaJimple.newClassConstant(annotationValue.toString(), identifierFactory);
return ConstantUtil.fromObject(classConstant, identifierFactory);
}
return ConstantUtil.fromObject(annotationValue);
return ConstantUtil.fromObject(annotationValue, identifierFactory);
}

private static Set<JavaSootField> resolveFields(
Expand All @@ -225,7 +230,7 @@ private static Set<JavaSootField> resolveFields(
.map(
field -> {
String fieldName = field.getName();
Type fieldType = DexUtil.toSootType(field.getType(), 0);
Type fieldType = DexUtil.toSootType(field.getType(), 0, signatureFactory);
FieldSignature fieldSignature =
signatureFactory.getFieldSignature(fieldName, classSignature, fieldType);
EnumSet<FieldModifier> modifiers =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,16 @@ public JavaSootMethod makeSootMethod(
if (Modifier.isAbstract(modifierFlags) || Modifier.isNative(modifierFlags)) {
List<Type> parameters =
method.getParameters().stream()
.map(methodParameter -> DexUtil.toSootType(methodParameter.getType(), 0))
.map(
methodParameter ->
DexUtil.toSootType(methodParameter.getType(), 0, view.getIdentifierFactory()))
.collect(Collectors.toList());
MethodSignature methodSignature =
view.getIdentifierFactory()
.getMethodSignature(
declaringclassType,
method.getName(),
DexUtil.toSootType(method.getReturnType(), 0),
DexUtil.toSootType(method.getReturnType(), 0, view.getIdentifierFactory()),
parameters);
DexMethodSource dexMethodSource =
new DexMethodSource(
Expand All @@ -76,7 +78,7 @@ public JavaSootMethod makeSootMethod(
view);
return dexMethodSource.makeSootMethod();
} else {
DexBody dexBody = new DexBody(method, dexEntry, declaringclassType);
DexBody dexBody = new DexBody(method, dexEntry, declaringclassType, view);
return dexBody.makeSootMethod(method, declaringclassType, bodyInterceptors, view);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ public void jimplify(DexBody body) {

Local castValue = body.getRegisterLocal(checkCastInstr.getRegisterA());
Type checkCastType =
DexUtil.toSootType(((TypeReference) checkCastInstr.getReference()).getType(), 0);
DexUtil.toSootType(
((TypeReference) checkCastInstr.getReference()).getType(),
0,
body.getIdentifierFactory());

JCastExpr castExpr = Jimple.newCastExpr(castValue, checkCastType);
// generate "x = (Type) x"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ public void jimplify(DexBody body) {
ReferenceInstruction constClass = (ReferenceInstruction) this.instruction;

TypeReference tidi = (TypeReference) (constClass.getReference());
ClassConstant classConstant = JavaJimple.newClassConstant(tidi.getType());
ClassConstant classConstant =
JavaJimple.newClassConstant(tidi.getType(), body.getIdentifierFactory());
int dest = ((OneRegisterInstruction) instruction).getRegisterA();

JAssignStmt jAssignStmt =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import sootup.core.jimple.basic.SimpleStmtPositionInfo;
import sootup.core.jimple.common.constant.StringConstant;
import sootup.core.jimple.common.stmt.JAssignStmt;
import sootup.java.core.JavaIdentifierFactory;

public class ConstStringInstruction extends DexLibAbstractInstruction {
public ConstStringInstruction(Instruction instruction, int codeAddress) {
Expand All @@ -54,7 +53,7 @@ public void jimplify(DexBody body) {
"Expected Instruction21c or Instruction31c but got neither.");
}
StringConstant stringConstant =
new StringConstant(s, JavaIdentifierFactory.getInstance().getType("java.lang.String"));
new StringConstant(s, body.getIdentifierFactory().getType("java.lang.String"));
JAssignStmt jAssignStmt =
Jimple.newAssignStmt(
body.getRegisterLocal(dest), stringConstant, new SimpleStmtPositionInfo(lineNumber));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.reference.FieldReference;
import sootup.apk.frontend.Util.DexUtil;
import sootup.core.IdentifierFactory;
import sootup.core.jimple.Jimple;
import sootup.core.jimple.basic.SimpleStmtPositionInfo;
import sootup.core.jimple.common.LValue;
Expand All @@ -47,13 +48,14 @@ public FieldInstruction(Instruction instruction, int codeAddress) {
super(instruction, codeAddress);
}

private JFieldRef getSootFieldRef(FieldReference fieldReference, boolean isStatic) {
private JFieldRef getSootFieldRef(
FieldReference fieldReference, boolean isStatic, IdentifierFactory identifierFactory) {
String className = DexUtil.dottedClassName(fieldReference.getDefiningClass());
FieldSignature fieldSignature =
new FieldSignature(
DexUtil.getClassTypeFromClassName(className),
identifierFactory.getFieldSignature(
fieldReference.getName(),
DexUtil.toSootType(fieldReference.getType(), 0));
DexUtil.getClassTypeFromClassName(className, identifierFactory),
DexUtil.toSootType(fieldReference.getType(), 0, identifierFactory));
if (isStatic) {
return new JStaticFieldRef(fieldSignature);
} else {
Expand All @@ -68,20 +70,23 @@ private JFieldRef getSootFieldRef(FieldReference fieldReference, boolean isStati
* Return a static SootFieldRef for a dexlib FieldReference.
*
* @param fref the dexlib FieldReference.
* @param identifierFactory the factory that creates the field signature
* @return the JFieldRef for the given field Reference
*/
protected JFieldRef getStaticSootFieldRef(FieldReference fref) {
return getSootFieldRef(fref, true);
protected JFieldRef getStaticSootFieldRef(
FieldReference fref, IdentifierFactory identifierFactory) {
return getSootFieldRef(fref, true, identifierFactory);
}

/**
* Return a SootFieldRef for a dexlib FieldReference.
*
* @return the JFieldRef for the given field Reference
* @param fref the dexlib FieldReference.
* @param identifierFactory the factory that creates the field signature
* @return the JFieldRef for the given field Reference
*/
protected JFieldRef getSootFieldRef(FieldReference fref) {
return getSootFieldRef(fref, false);
protected JFieldRef getSootFieldRef(FieldReference fref, IdentifierFactory identifierFactory) {
return getSootFieldRef(fref, false, identifierFactory);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ private NumericConstant getArrayElement(Number element, DexBody dexBody, int arr
ArrayType arrayType =
(ArrayType)
DexUtil.toSootType(
((TypeReference) instruction22c.getReference()).getType(), 0);
((TypeReference) instruction22c.getReference()).getType(),
0,
dexBody.getIdentifierFactory());
elementType = arrayType.getElementType();
break Outer;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import sootup.core.jimple.common.stmt.JAssignStmt;
import sootup.core.types.ArrayType;
import sootup.core.types.Type;
import sootup.java.core.JavaIdentifierFactory;
import sootup.java.core.language.JavaJimple;

public class FilledNewArrayInstruction extends FilledArrayInstruction {
Expand All @@ -52,12 +51,16 @@ public void jimplify(DexBody body) {
};
int usedRegister = filledNewArrayInstr.getRegisterCount();

Type t = DexUtil.toSootType(((TypeReference) filledNewArrayInstr.getReference()).getType(), 0);
Type t =
DexUtil.toSootType(
((TypeReference) filledNewArrayInstr.getReference()).getType(),
0,
body.getIdentifierFactory());
// NewArrayExpr needs the ElementType as it increases the array dimension by 1
Type arrayType = ((ArrayType) t).getElementType();
JNewArrayExpr arrayExpr =
JavaJimple.newNewArrayExpr(
arrayType, IntConstant.getInstance(usedRegister), JavaIdentifierFactory.getInstance());
arrayType, IntConstant.getInstance(usedRegister), body.getIdentifierFactory());
// new local generated intentional, will be moved to real register by MoveResult
Local arrayLocal = body.getStoreResultLocal();
JAssignStmt assign =
Expand Down
Loading
Loading