diff --git a/src/main/java/jadx/codegen/InsnGen.java b/src/main/java/jadx/codegen/InsnGen.java index 84d497830490ec466b9c89ef08710364b1c25fd9..092b9c860e0a8da763df29f596817892568bee9b 100644 --- a/src/main/java/jadx/codegen/InsnGen.java +++ b/src/main/java/jadx/codegen/InsnGen.java @@ -94,10 +94,18 @@ public class InsnGen { } public String declareVar(RegisterArg arg) throws CodegenException { - String type = TypeGen.translate(mgen.getClassGen(), arg.getType()); - String generic = arg.getType().getGeneric(); - if (generic != null) - type += " /* " + generic + " */"; + String type = useType(arg.getType()); + ArgType[] generics = arg.getType().getGenericTypes(); + if (generics != null) { + StringBuilder sb = new StringBuilder(); + sb.append(type); + sb.append("/*<"); + for (ArgType gt : generics) { + sb.append(useType(gt)); + } + sb.append(">*/"); + type = sb.toString(); + } return type + " " + arg(arg); } diff --git a/src/main/java/jadx/dex/info/LocalVarInfo.java b/src/main/java/jadx/dex/info/LocalVarInfo.java index f86c6db1baf17de29b764e598be9681a0c6798cb..412296fa19a80e38093639e335ceaa4ec5891012 100644 --- a/src/main/java/jadx/dex/info/LocalVarInfo.java +++ b/src/main/java/jadx/dex/info/LocalVarInfo.java @@ -28,7 +28,7 @@ public class LocalVarInfo extends RegisterArg { private void init(String name, ArgType type, String sign) { if (sign != null) { - type.setGeneric(sign); + type = ArgType.generic(type.getObject(), sign); } TypedVar tv = new TypedVar(type); tv.setName(name); diff --git a/src/main/java/jadx/dex/instructions/args/ArgType.java b/src/main/java/jadx/dex/instructions/args/ArgType.java index bd459743cade497f8ab58bcbb7bfd56d9d91686b..77e393077ceb8877993b0363350c67d0e6b1be46 100644 --- a/src/main/java/jadx/dex/instructions/args/ArgType.java +++ b/src/main/java/jadx/dex/instructions/args/ArgType.java @@ -7,8 +7,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public final class ArgType { - +public abstract class ArgType { public static final ArgType INT = primitive(PrimitiveType.INT); public static final ArgType BOOLEAN = primitive(PrimitiveType.BOOLEAN); public static final ArgType BYTE = primitive(PrimitiveType.BYTE); @@ -35,113 +34,248 @@ public final class ArgType { public static final ArgType UNKNOWN_OBJECT = unknown(PrimitiveType.OBJECT, PrimitiveType.ARRAY); - private final PrimitiveType type; - private final String object; - private final ArgType arrayElement; - private final PrimitiveType possibleTypes[]; + protected int hash; - private final int hash; + private static ArgType primitive(PrimitiveType stype) { + return new PrimitiveArg(stype); + } - private String generic; // TODO extract generic info from signature + public static ArgType object(String obj) { + return new ObjectArg(Utils.cleanObjectName(obj)); + } - private ArgType(PrimitiveType type, String object, ArgType arrayElement) { - this.type = type; - this.object = (object == null ? null : Utils.cleanObjectName(object)); - this.arrayElement = arrayElement; - this.possibleTypes = null; - this.hash = calcHashCode(); + public static ArgType generic(String obj, String signature) { + return new GenericObjectArg(obj, signature); } - private ArgType(PrimitiveType[] posTypes) { - this.type = null; - this.object = null; - this.arrayElement = null; - this.possibleTypes = posTypes; - this.hash = calcHashCode(); + public static ArgType array(ArgType vtype) { + return new ArrayArg(vtype); } - public static ArgType primitive(PrimitiveType stype) { - assert stype != PrimitiveType.OBJECT && stype != PrimitiveType.ARRAY; - return new ArgType(stype, null, null); + public static ArgType unknown(PrimitiveType... types) { + return new UnknownArg(types); } - public static ArgType object(String obj) { - assert obj != null; - return new ArgType(PrimitiveType.OBJECT, obj, null); + private static abstract class KnownTypeArg extends ArgType { + @Override + public boolean isTypeKnown() { + return true; + } } - public static ArgType array(ArgType vtype) { - return new ArgType(PrimitiveType.ARRAY, null, vtype); + private static final class PrimitiveArg extends KnownTypeArg { + private final PrimitiveType type; + + public PrimitiveArg(PrimitiveType type) { + this.type = type; + this.hash = type.hashCode(); + } + + @Override + public PrimitiveType getPrimitiveType() { + return type; + } + + @Override + public boolean isPrimitive() { + return true; + } + + @Override + public String toString() { + return type.toString(); + } } - public static ArgType unknown(PrimitiveType... types) { - return new ArgType(types); + private static class ObjectArg extends KnownTypeArg { + private final String object; + + public ObjectArg(String obj) { + this.object = obj; + this.hash = obj.hashCode(); + } + + @Override + public String getObject() { + return object; + } + + @Override + public boolean isObject() { + return true; + } + + @Override + public PrimitiveType getPrimitiveType() { + return PrimitiveType.OBJECT; + } + + @Override + public String toString() { + return object; + } + } + + private static final class GenericObjectArg extends ObjectArg { + private final ArgType[] generics; + + public GenericObjectArg(String obj, String signature) { + super(obj); + this.generics = parseSignature(signature); + this.hash = obj.hashCode() + 31 * Arrays.hashCode(generics); + } + + @Override + public ArgType[] getGenericTypes() { + return generics; + } + + @Override + public String toString() { + return super.toString() + "<" + Arrays.toString(generics) + ">"; + } + } + + private static final class ArrayArg extends KnownTypeArg { + private final ArgType arrayElement; + + public ArrayArg(ArgType arrayElement) { + this.arrayElement = arrayElement; + this.hash = arrayElement.hashCode(); + } + + @Override + public ArgType getArrayElement() { + return arrayElement; + } + + @Override + public boolean isArray() { + return true; + } + + @Override + public PrimitiveType getPrimitiveType() { + return PrimitiveType.ARRAY; + } + + @Override + public int getArrayDimension() { + if (isArray()) + return 1 + arrayElement.getArrayDimension(); + else + return 0; + } + + @Override + public ArgType getArrayRootElement() { + if (isArray()) + return arrayElement.getArrayRootElement(); + else + return this; + } + + @Override + public String toString() { + return arrayElement.toString(); + } + } + + private static final class UnknownArg extends ArgType { + private final PrimitiveType possibleTypes[]; + + public UnknownArg(PrimitiveType[] types) { + this.possibleTypes = types; + this.hash = Arrays.hashCode(possibleTypes); + } + + @Override + public PrimitiveType[] getPossibleTypes() { + return possibleTypes; + } + + @Override + public boolean isTypeKnown() { + return false; + } + + @Override + public boolean contains(PrimitiveType type) { + for (PrimitiveType t : possibleTypes) + if (t == type) + return true; + return false; + } + + @Override + public ArgType selectFirst() { + assert possibleTypes != null; + PrimitiveType f = possibleTypes[0]; + if (f == PrimitiveType.OBJECT || f == PrimitiveType.ARRAY) + return object(Consts.CLASS_OBJECT); + else + return primitive(f); + } + + @Override + public String toString() { + if (possibleTypes.length == PrimitiveType.values().length) + return "*"; + else + return "?" + Arrays.toString(possibleTypes); + } } public boolean isTypeKnown() { - return type != null; + return false; } public PrimitiveType getPrimitiveType() { - return type; + return null; } public boolean isPrimitive() { - return type != null && type != PrimitiveType.OBJECT && type != PrimitiveType.ARRAY; + return false; } public String getObject() { - return object; + throw new UnsupportedOperationException(); } public boolean isObject() { - return type == PrimitiveType.OBJECT; - } - - public String getGeneric() { - return generic; + return false; } - public void setGeneric(String generic) { - this.generic = generic; + public ArgType[] getGenericTypes() { + return null; } public ArgType getArrayElement() { - return arrayElement; + return null; } public boolean isArray() { - return type == PrimitiveType.ARRAY; + return false; } public int getArrayDimension() { - if (isArray()) - return 1 + arrayElement.getArrayDimension(); - else - return 0; + return 0; } public ArgType getArrayRootElement() { - if (isArray()) - return arrayElement.getArrayRootElement(); - else - return this; + return this; } public boolean contains(PrimitiveType type) { - for (PrimitiveType t : possibleTypes) - if (t == type) - return true; - return false; + throw new UnsupportedOperationException(); } public ArgType selectFirst() { - assert possibleTypes != null; - PrimitiveType f = possibleTypes[0]; - if (f == PrimitiveType.OBJECT || f == PrimitiveType.ARRAY) - return object(Consts.CLASS_OBJECT); - else - return primitive(f); + throw new UnsupportedOperationException(); + } + + public PrimitiveType[] getPossibleTypes() { + return null; } public static ArgType merge(ArgType a, ArgType b) { @@ -161,7 +295,7 @@ public final class ArgType { if (a == UNKNOWN) return b; - if (a.possibleTypes != null) { + if (!a.isTypeKnown()) { if (b.isTypeKnown()) { if (a.contains(b.getPrimitiveType())) return b; @@ -170,7 +304,7 @@ public final class ArgType { } else { // both types unknown List types = new ArrayList(); - for (PrimitiveType type : a.possibleTypes) { + for (PrimitiveType type : a.getPossibleTypes()) { if (b.contains(type)) types.add(type); } @@ -188,9 +322,12 @@ public final class ArgType { } } else { if (a.isObject() && b.isObject()) { - if (a.getObject().equals(b.getObject())) - return a; - else if (a.getObject().equals(OBJECT.getObject())) + if (a.getObject().equals(b.getObject())) { + if (a.getGenericTypes() != null) + return a; + else + return b; + } else if (a.getObject().equals(OBJECT.getObject())) return b; else if (b.getObject().equals(OBJECT.getObject())) return a; @@ -217,12 +354,39 @@ public final class ArgType { public static ArgType parse(String type) { assert type.length() > 0 : "Empty type"; char f = type.charAt(0); - if (f == 'L') + if (f == 'L') { return object(type); - else if (f == '[') + } else if (f == '[') { return array(parse(type.substring(1))); - else + } else { return parse(f); + } + } + + public static ArgType[] parseSignature(String signature) { + int b = signature.indexOf('<') + 1; + int e = signature.lastIndexOf('>'); + String gens = signature.substring(b, e); + String[] split = gens.split(";"); + ArgType[] result = new ArgType[split.length]; + for (int i = 0; i < split.length; i++) { + String g = split[i]; + switch (g.charAt(0)) { + case 'L': + result[i] = object(g + ";"); + break; + + case '*': + case '?': + result[i] = UNKNOWN; + break; + + default: + result[i] = UNKNOWN_OBJECT; + break; + } + } + return result; } private static ArgType parse(char f) { @@ -250,37 +414,17 @@ public final class ArgType { } public int getRegCount() { - if (type == PrimitiveType.LONG || type == PrimitiveType.DOUBLE) - return 2; - else - return 1; + if (isPrimitive()) { + PrimitiveType type = getPrimitiveType(); + if (type == PrimitiveType.LONG || type == PrimitiveType.DOUBLE) + return 2; + } + return 1; } @Override public String toString() { - if (this == UNKNOWN) - return "ANY"; - - if (type != null) { - if (type == PrimitiveType.OBJECT) - return object; - else if (type == PrimitiveType.ARRAY) - return arrayElement + "[]"; - else - return type.toString(); - } else { - return "?" + Arrays.asList(possibleTypes).toString(); - } - } - - private int calcHashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((type == null) ? 0 : type.hashCode()); - result = prime * result + ((object == null) ? 0 : object.hashCode()); - result = prime * result + ((arrayElement == null) ? 0 : arrayElement.hashCode()); - result = prime * result + Arrays.hashCode(possibleTypes); - return result; + return "UNKNOWN"; } @Override @@ -293,16 +437,13 @@ public final class ArgType { if (this == obj) return true; if (obj == null) return false; if (hash != obj.hashCode()) return false; - if (getClass() != obj.getClass()) return false; - ArgType other = (ArgType) obj; - if (type != other.type) return false; - if (!Arrays.equals(possibleTypes, other.possibleTypes)) return false; - if (arrayElement == null) { - if (other.arrayElement != null) return false; - } else if (!arrayElement.equals(other.arrayElement)) return false; - if (object == null) { - if (other.object != null) return false; - } else if (!object.equals(other.object)) return false; + if (getClass() != obj.getClass()) { + return false; + } + // TODO: don't use toString + if (!toString().equals(obj.toString())) { + return false; + } return true; } diff --git a/src/main/java/jadx/dex/instructions/args/PrimitiveType.java b/src/main/java/jadx/dex/instructions/args/PrimitiveType.java index 9af332f4b741d4e169d5988e56572ffa4a270e15..dca48f734151910f7670c83c60642aef05172627 100644 --- a/src/main/java/jadx/dex/instructions/args/PrimitiveType.java +++ b/src/main/java/jadx/dex/instructions/args/PrimitiveType.java @@ -45,6 +45,6 @@ public enum PrimitiveType { @Override public String toString() { - return this.name().toLowerCase(); + return longName; } } diff --git a/src/main/java/jadx/dex/nodes/MethodNode.java b/src/main/java/jadx/dex/nodes/MethodNode.java index df4bc7bec62491dcb5687f3d0db853d74e8a22e5..983cffa9934659a927f9ad309366764da01d53dd 100644 --- a/src/main/java/jadx/dex/nodes/MethodNode.java +++ b/src/main/java/jadx/dex/nodes/MethodNode.java @@ -109,8 +109,6 @@ public class MethodNode extends AttrNode implements ILoadable { return; if (instructions != null) instructions.clear(); - // if (blocks != null) blocks.clear(); - // if (exitBlocks != null) exitBlocks.clear(); blocks = null; exitBlocks = null; if (exceptionHandlers != null) exceptionHandlers.clear();