Skip to content

Commit

Permalink
fix: several improvements for generics and type inference
Browse files Browse the repository at this point in the history
- support 'extends' for generic type variables
- insert cast instructions to help type inference (#956)
- correct move instructions insertion (to resolve types in PHI)

Signed-off-by: Skylot <skylot@gmail.com>
  • Loading branch information
skylot committed Jul 31, 2020
1 parent bcd6e53 commit 459f12d
Show file tree
Hide file tree
Showing 48 changed files with 824 additions and 475 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package jadx.core.clsp;
package jadx.cli.clst;

import java.io.IOException;
import java.nio.file.Path;
Expand All @@ -16,6 +16,7 @@
import jadx.api.plugins.JadxPluginManager;
import jadx.api.plugins.input.JadxInputPlugin;
import jadx.api.plugins.input.data.ILoadResult;
import jadx.core.clsp.ClsSet;
import jadx.core.dex.nodes.RootNode;

/**
Expand Down Expand Up @@ -50,6 +51,7 @@ public static void main(String[] args) throws IOException {
ClsSet set = new ClsSet(root);
set.loadFrom(root);
set.save(output);

LOG.info("Output: {}, file size: {}B", output, output.toFile().length());
LOG.info("done");
}
Expand Down
Binary file modified jadx-core/clsp-data/android-29-clst.jar
Binary file not shown.
1 change: 1 addition & 0 deletions jadx-core/src/main/java/jadx/core/Consts.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class Consts {
public static final String CLASS_STRING = "java.lang.String";
public static final String CLASS_CLASS = "java.lang.Class";
public static final String CLASS_THROWABLE = "java.lang.Throwable";
public static final String CLASS_EXCEPTION = "java.lang.Exception";
public static final String CLASS_ENUM = "java.lang.Enum";

public static final String CLASS_STRING_BUILDER = "java.lang.StringBuilder";
Expand Down
64 changes: 13 additions & 51 deletions jadx-core/src/main/java/jadx/core/clsp/ClsSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,13 @@
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.GenericTypeParameter;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.core.utils.files.ZipSecurity;

import static jadx.core.utils.Utils.isEmpty;
import static jadx.core.utils.Utils.notEmpty;

/**
Expand Down Expand Up @@ -139,7 +137,7 @@ private void processMethodDetails(MethodNode mth, List<ClspMethod> methods) {
ArgType genericRetType = mth.getReturnType();
boolean varArgs = accessFlags.isVarArgs();
List<ArgType> throwList = mth.getThrows();
List<GenericTypeParameter> typeParameters = mth.getTypeParameters();
List<ArgType> typeParameters = mth.getTypeParameters();
// add only methods with additional info
if (varArgs
|| notEmpty(throwList)
Expand Down Expand Up @@ -186,7 +184,7 @@ private static ClspClass getCls(String fullName, Map<String, ClspClass> names) {
return cls;
}

void save(Path path) throws IOException {
public void save(Path path) throws IOException {
FileUtils.makeDirsForFile(path);
String outputName = path.getFileName().toString();
if (outputName.endsWith(CLST_EXTENSION)) {
Expand Down Expand Up @@ -225,7 +223,7 @@ void save(Path path) throws IOException {
}
}

public void save(OutputStream output) throws IOException {
private void save(OutputStream output) throws IOException {
DataOutputStream out = new DataOutputStream(output);
out.writeBytes(JADX_CLS_SET_HEADER);
out.writeByte(VERSION);
Expand All @@ -239,7 +237,7 @@ public void save(OutputStream output) throws IOException {
}
for (ClspClass cls : classes) {
writeArgTypesArray(out, cls.getParents(), names);
writeGenericTypeParameters(out, cls.getTypeParameters(), names);
writeArgTypesList(out, cls.getTypeParameters(), names);
List<ClspMethod> methods = cls.getSortedMethodsList();
out.writeShort(methods.size());
for (ClspMethod method : methods) {
Expand All @@ -250,20 +248,6 @@ public void save(OutputStream output) throws IOException {
LOG.info("Classes: {}, methods: {}, file size: {}B", classes.length, methodsCount, out.size());
}

private static void writeGenericTypeParameters(DataOutputStream out,
List<GenericTypeParameter> typeParameters,
Map<String, ClspClass> names) throws IOException {
if (isEmpty(typeParameters)) {
out.writeByte(0);
} else {
writeUnsignedByte(out, typeParameters.size());
for (GenericTypeParameter typeParameter : typeParameters) {
writeArgType(out, typeParameter.getTypeVariable(), names);
writeArgTypesList(out, typeParameter.getExtendsList(), names);
}
}
}

private static void writeMethod(DataOutputStream out, ClspMethod method, Map<String, ClspClass> names) throws IOException {
MethodInfo methodInfo = method.getMethodInfo();
writeString(out, methodInfo.getName());
Expand All @@ -272,7 +256,7 @@ private static void writeMethod(DataOutputStream out, ClspMethod method, Map<Str

writeArgTypesList(out, method.containsGenericArgs() ? method.getArgTypes() : Collections.emptyList(), names);
writeArgType(out, method.getReturnType(), names);
writeGenericTypeParameters(out, method.getTypeParameters(), names);
writeArgTypesList(out, method.getTypeParameters(), names);
out.writeBoolean(method.isVarArg());
writeArgTypesList(out, method.getThrows(), names);
}
Expand Down Expand Up @@ -323,11 +307,11 @@ private static void writeArgType(DataOutputStream out, ArgType argType, Map<Stri
} else if (argType.isGeneric()) {
out.writeByte(TypeEnum.GENERIC.ordinal());
out.writeInt(getCls(argType, names).getId());
ArgType[] types = argType.getGenericTypes();
writeArgTypesArray(out, types, names);
writeArgTypesList(out, argType.getGenericTypes(), names);
} else if (argType.isGenericType()) {
out.writeByte(TypeEnum.GENERIC_TYPE_VARIABLE.ordinal());
writeString(out, argType.getObject());
writeArgTypesList(out, argType.getExtendTypes(), names);
} else if (argType.isObject()) {
out.writeByte(TypeEnum.OBJECT.ordinal());
out.writeInt(getCls(argType, names).getId());
Expand Down Expand Up @@ -380,26 +364,12 @@ private void load(InputStream input) throws IOException, DecodeException {
ClspClass nClass = classes[i];
ClassInfo clsInfo = ClassInfo.fromType(root, nClass.getClsType());
nClass.setParents(readArgTypesArray(in));
nClass.setTypeParameters(readGenericTypeParameters(in));
nClass.setTypeParameters(readArgTypesList(in));
nClass.setMethods(readClsMethods(in, clsInfo));
}
}
}

private List<GenericTypeParameter> readGenericTypeParameters(DataInputStream in) throws IOException {
int count = readUnsignedByte(in);
if (count == 0) {
return Collections.emptyList();
}
List<GenericTypeParameter> list = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
ArgType typeVariable = readArgType(in);
List<ArgType> extendsList = readArgTypesList(in);
list.add(new GenericTypeParameter(typeVariable, extendsList));
}
return list;
}

private List<ClspMethod> readClsMethods(DataInputStream in, ClassInfo clsInfo) throws IOException {
int mCount = in.readShort();
List<ClspMethod> methods = new ArrayList<>(mCount);
Expand All @@ -421,7 +391,7 @@ private ClspMethod readMethod(DataInputStream in, ClassInfo clsInfo) throws IOEx
if (Objects.equals(genericRetType, retType)) {
genericRetType = retType;
}
List<GenericTypeParameter> typeParameters = readGenericTypeParameters(in);
List<ArgType> typeParameters = readArgTypesList(in);
boolean varArgs = in.readBoolean();
List<ArgType> throwList = readArgTypesList(in);
MethodInfo methodInfo = MethodInfo.fromDetails(root, clsInfo, name, argTypes, retType);
Expand Down Expand Up @@ -482,20 +452,12 @@ private ArgType readArgType(DataInputStream in) throws IOException {

case GENERIC:
ArgType clsType = classes[in.readInt()].getClsType();
int typeLength = readUnsignedByte(in);
ArgType[] generics;
if (typeLength == 0) {
generics = null;
} else {
generics = new ArgType[typeLength];
for (int i = 0; i < typeLength; i++) {
generics[i] = readArgType(in);
}
}
return ArgType.generic(clsType, generics);
return ArgType.generic(clsType, readArgTypesList(in));

case GENERIC_TYPE_VARIABLE:
return ArgType.genericType(readString(in));
String typeVar = readString(in);
List<ArgType> extendTypes = readArgTypesList(in);
return ArgType.genericType(typeVar, extendTypes);

case OBJECT:
return classes[in.readInt()].getClsType();
Expand Down
7 changes: 3 additions & 4 deletions jadx-core/src/main/java/jadx/core/clsp/ClspClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.util.Objects;

import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.GenericTypeParameter;

/**
* Class node in classpath graph
Expand All @@ -19,7 +18,7 @@ public class ClspClass {
private final int id;
private ArgType[] parents;
private Map<String, ClspMethod> methodsMap = Collections.emptyMap();
private List<GenericTypeParameter> typeParameters = Collections.emptyList();
private List<ArgType> typeParameters = Collections.emptyList();

public ClspClass(ArgType clsType, int id) {
this.clsType = clsType;
Expand Down Expand Up @@ -69,11 +68,11 @@ public void setMethods(List<ClspMethod> methods) {
setMethodsMap(map);
}

public List<GenericTypeParameter> getTypeParameters() {
public List<ArgType> getTypeParameters() {
return typeParameters;
}

public void setTypeParameters(List<GenericTypeParameter> typeParameters) {
public void setTypeParameters(List<ArgType> typeParameters) {
this.typeParameters = typeParameters;
}

Expand Down
7 changes: 3 additions & 4 deletions jadx-core/src/main/java/jadx/core/clsp/ClspMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.GenericTypeParameter;
import jadx.core.dex.nodes.IMethodDetails;
import jadx.core.utils.Utils;

Expand All @@ -19,13 +18,13 @@ public class ClspMethod implements IMethodDetails, Comparable<ClspMethod> {
private final MethodInfo methodInfo;
private final List<ArgType> argTypes;
private final ArgType returnType;
private final List<GenericTypeParameter> typeParameters;
private final List<ArgType> typeParameters;
private final List<ArgType> throwList;
private final boolean varArg;

public ClspMethod(MethodInfo methodInfo,
List<ArgType> argTypes, ArgType returnType,
List<GenericTypeParameter> typeParameters,
List<ArgType> typeParameters,
boolean varArgs, List<ArgType> throwList) {
this.methodInfo = methodInfo;
this.argTypes = argTypes;
Expand Down Expand Up @@ -59,7 +58,7 @@ public int getArgsCount() {
}

@Override
public List<GenericTypeParameter> getTypeParameters() {
public List<ArgType> getTypeParameters() {
return typeParameters;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.GenericTypeParameter;
import jadx.core.dex.nodes.IMethodDetails;

public class SimpleMethodDetails implements IMethodDetails {
Expand All @@ -32,7 +31,7 @@ public List<ArgType> getArgTypes() {
}

@Override
public List<GenericTypeParameter> getTypeParameters() {
public List<ArgType> getTypeParameters() {
return Collections.emptyList();
}

Expand Down
20 changes: 9 additions & 11 deletions jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import jadx.core.dex.instructions.mods.ConstructorInsn;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.GenericTypeParameter;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
Expand Down Expand Up @@ -189,23 +188,22 @@ public void addClassDeclaration(CodeWriter clsCode) {
}
}

public boolean addGenericTypeParameters(CodeWriter code, List<GenericTypeParameter> generics, boolean classDeclaration) {
public boolean addGenericTypeParameters(CodeWriter code, List<ArgType> generics, boolean classDeclaration) {
if (generics == null || generics.isEmpty()) {
return false;
}
code.add('<');
int i = 0;
for (GenericTypeParameter genericInfo : generics) {
for (ArgType genericInfo : generics) {
if (i != 0) {
code.add(", ");
}
ArgType type = genericInfo.getTypeVariable();
if (type.isGenericType()) {
code.add(type.getObject());
if (genericInfo.isGenericType()) {
code.add(genericInfo.getObject());
} else {
useClass(code, type);
useClass(code, genericInfo);
}
List<ArgType> list = genericInfo.getExtendsList();
List<ArgType> list = genericInfo.getExtendTypes();
if (list != null && !list.isEmpty()) {
code.add(" extends ");
for (Iterator<ArgType> it = list.iterator(); it.hasNext();) {
Expand Down Expand Up @@ -517,15 +515,15 @@ public void useClass(CodeWriter code, ArgType type) {
}

useClass(code, ClassInfo.fromType(cls.root(), type));
ArgType[] generics = type.getGenericTypes();
List<ArgType> generics = type.getGenericTypes();
if (generics != null) {
code.add('<');
int len = generics.length;
int len = generics.size();
for (int i = 0; i < len; i++) {
if (i != 0) {
code.add(", ");
}
ArgType gt = generics[i];
ArgType gt = generics.get(i);
ArgType wt = gt.getWildcardType();
if (wt != null) {
ArgType.WildcardBound bound = gt.getWildcardBound();
Expand Down
2 changes: 1 addition & 1 deletion jadx-core/src/main/java/jadx/core/codegen/MethodGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ private void addMethodArguments(CodeWriter code, List<RegisterArg> args) {
CodeVar var;
if (ssaVar == null) {
// null for abstract or interface methods
var = CodeVar.fromMthArg(mthArg);
var = CodeVar.fromMthArg(mthArg, classGen.isFallbackMode());
} else {
var = ssaVar.getCodeVar();
}
Expand Down
27 changes: 11 additions & 16 deletions jadx-core/src/main/java/jadx/core/codegen/NameGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,13 @@ private void addNamesUsedInClass() {
}

public String assignArg(CodeVar var) {
String name = makeArgName(var);
if (fallback) {
return name;
return getFallbackName(var);
}
name = getUniqueVarName(name);
if (var.isThis()) {
return RegisterArg.THIS_ARG_NAME;
}
String name = getUniqueVarName(makeArgName(var));
var.setName(name);
return name;
}
Expand Down Expand Up @@ -118,24 +120,17 @@ private String getUniqueVarName(String name) {
}

private String makeArgName(CodeVar var) {
if (fallback) {
return getFallbackName(var);
}
if (var.isThis()) {
return RegisterArg.THIS_ARG_NAME;
}
String name = var.getName();
String varName = name != null ? name : guessName(var);
if (NameMapper.isReserved(varName)) {
varName = varName + 'R';
if (name == null) {
name = guessName(var);
}
if (!NameMapper.isValidAndPrintable(varName)) {
varName = getFallbackName(var);
if (!NameMapper.isValidAndPrintable(name)) {
name = getFallbackName(var);
}
if (Consts.DEBUG) {
varName += '_' + getFallbackName(var);
name += '_' + getFallbackName(var);
}
return varName;
return name;
}

private String getFallbackName(CodeVar var) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public enum AFlag {
*/
EXPLICIT_PRIMITIVE_TYPE,
EXPLICIT_CAST,
SOFT_CAST, // synthetic cast to help type inference

INCONSISTENT_CODE, // warning about incorrect decompilation
}
Loading

0 comments on commit 459f12d

Please sign in to comment.