Commit 0a1981f9 authored by Skylot's avatar Skylot

gui: add hyperlinks for classes and fields

parent 0a36bfb0
...@@ -30,6 +30,10 @@ public final class CodePosition { ...@@ -30,6 +30,10 @@ public final class CodePosition {
return offset; return offset;
} }
public boolean isSet() {
return line != 0 || offset != 0;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {
......
...@@ -73,7 +73,7 @@ public class AnnotationGen { ...@@ -73,7 +73,7 @@ public class AnnotationGen {
private void formatAnnotation(CodeWriter code, Annotation a) { private void formatAnnotation(CodeWriter code, Annotation a) {
code.add('@'); code.add('@');
code.add(classGen.useClass(a.getType())); classGen.useType(code, a.getType());
Map<String, Object> vl = a.getValues(); Map<String, Object> vl = a.getValues();
if (!vl.isEmpty()) { if (!vl.isEmpty()) {
code.add('('); code.add('(');
...@@ -102,7 +102,7 @@ public class AnnotationGen { ...@@ -102,7 +102,7 @@ public class AnnotationGen {
code.add(" throws "); code.add(" throws ");
for (Iterator<ArgType> it = ((List<ArgType>) exs).iterator(); it.hasNext(); ) { for (Iterator<ArgType> it = ((List<ArgType>) exs).iterator(); it.hasNext(); ) {
ArgType ex = it.next(); ArgType ex = it.next();
code.add(TypeGen.translate(classGen, ex)); classGen.useType(code, ex);
if (it.hasNext()) { if (it.hasNext()) {
code.add(", "); code.add(", ");
} }
...@@ -144,11 +144,12 @@ public class AnnotationGen { ...@@ -144,11 +144,12 @@ public class AnnotationGen {
} else if (val instanceof Byte) { } else if (val instanceof Byte) {
code.add(TypeGen.formatByte((Byte) val)); code.add(TypeGen.formatByte((Byte) val));
} else if (val instanceof ArgType) { } else if (val instanceof ArgType) {
code.add(TypeGen.translate(classGen, (ArgType) val)).add(".class"); classGen.useType(code, (ArgType) val);
code.add(".class");
} else if (val instanceof FieldInfo) { } else if (val instanceof FieldInfo) {
// must be a static field // must be a static field
FieldInfo field = (FieldInfo) val; FieldInfo field = (FieldInfo) val;
code.add(InsnGen.makeStaticFieldAccess(field, classGen)); InsnGen.makeStaticFieldAccess(code, field, classGen);
} else if (val instanceof List) { } else if (val instanceof List) {
code.add('{'); code.add('{');
Iterator<?> it = ((List) val).iterator(); Iterator<?> it = ((List) val).iterator();
......
...@@ -11,6 +11,7 @@ import jadx.core.dex.info.AccessInfo; ...@@ -11,6 +11,7 @@ import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.FieldNode;
...@@ -130,7 +131,9 @@ public class ClassGen { ...@@ -130,7 +131,9 @@ public class ClassGen {
if (sup != null if (sup != null
&& !sup.getFullName().equals(Consts.CLASS_OBJECT) && !sup.getFullName().equals(Consts.CLASS_OBJECT)
&& !sup.getFullName().equals(Consts.CLASS_ENUM)) { && !sup.getFullName().equals(Consts.CLASS_ENUM)) {
clsCode.add("extends ").add(useClass(sup)).add(' '); clsCode.add("extends ");
useClass(clsCode, sup);
clsCode.add(' ');
} }
if (cls.getInterfaces().size() > 0 && !af.isAnnotation()) { if (cls.getInterfaces().size() > 0 && !af.isAnnotation()) {
...@@ -141,7 +144,7 @@ public class ClassGen { ...@@ -141,7 +144,7 @@ public class ClassGen {
} }
for (Iterator<ClassInfo> it = cls.getInterfaces().iterator(); it.hasNext(); ) { for (Iterator<ClassInfo> it = cls.getInterfaces().iterator(); it.hasNext(); ) {
ClassInfo interf = it.next(); ClassInfo interf = it.next();
clsCode.add(useClass(interf)); useClass(clsCode, interf);
if (it.hasNext()) { if (it.hasNext()) {
clsCode.add(", "); clsCode.add(", ");
} }
...@@ -165,12 +168,20 @@ public class ClassGen { ...@@ -165,12 +168,20 @@ public class ClassGen {
if (i != 0) { if (i != 0) {
code.add(", "); code.add(", ");
} }
code.add(useClass(type)); if (type.isGenericType()) {
code.add(type.getObject());
} else {
useClass(code, ClassInfo.fromType(type));
}
if (list != null && !list.isEmpty()) { if (list != null && !list.isEmpty()) {
code.add(" extends "); code.add(" extends ");
for (Iterator<ArgType> it = list.iterator(); it.hasNext(); ) { for (Iterator<ArgType> it = list.iterator(); it.hasNext(); ) {
ArgType g = it.next(); ArgType g = it.next();
code.add(useClass(g)); if (g.isGenericType()) {
code.add(g.getObject());
} else {
useClass(code, ClassInfo.fromType(g));
}
if (it.hasNext()) { if (it.hasNext()) {
code.add(" & "); code.add(" & ");
} }
...@@ -259,7 +270,7 @@ public class ClassGen { ...@@ -259,7 +270,7 @@ public class ClassGen {
} }
annotationGen.addForField(code, f); annotationGen.addForField(code, f);
code.startLine(f.getAccessFlags().makeString()); code.startLine(f.getAccessFlags().makeString());
code.add(TypeGen.translate(this, f.getType())); useType(code, f.getType());
code.add(' '); code.add(' ');
code.add(f.getName()); code.add(f.getName());
FieldValueAttr fv = f.get(AType.FIELD_VALUE); FieldValueAttr fv = f.get(AType.FIELD_VALUE);
...@@ -278,80 +289,91 @@ public class ClassGen { ...@@ -278,80 +289,91 @@ public class ClassGen {
private void addEnumFields(CodeWriter code) throws CodegenException { private void addEnumFields(CodeWriter code) throws CodegenException {
EnumClassAttr enumFields = cls.get(AType.ENUM_CLASS); EnumClassAttr enumFields = cls.get(AType.ENUM_CLASS);
if (enumFields != null) { if (enumFields == null) {
InsnGen igen = null; return;
for (Iterator<EnumField> it = enumFields.getFields().iterator(); it.hasNext(); ) { }
EnumField f = it.next(); InsnGen igen = null;
code.startLine(f.getName()); for (Iterator<EnumField> it = enumFields.getFields().iterator(); it.hasNext(); ) {
if (f.getArgs().size() != 0) { EnumField f = it.next();
code.add('('); code.startLine(f.getName());
for (Iterator<InsnArg> aIt = f.getArgs().iterator(); aIt.hasNext(); ) { if (f.getArgs().size() != 0) {
InsnArg arg = aIt.next(); code.add('(');
if (igen == null) { for (Iterator<InsnArg> aIt = f.getArgs().iterator(); aIt.hasNext(); ) {
// don't init mth gen if this is simple enum InsnArg arg = aIt.next();
MethodGen mthGen = new MethodGen(this, enumFields.getStaticMethod()); if (igen == null) {
igen = new InsnGen(mthGen, false); // don't init mth gen if this is simple enum
} MethodGen mthGen = new MethodGen(this, enumFields.getStaticMethod());
igen.addArg(code, arg); igen = new InsnGen(mthGen, false);
if (aIt.hasNext()) { }
code.add(", "); igen.addArg(code, arg);
} if (aIt.hasNext()) {
code.add(", ");
} }
code.add(')');
}
if (f.getCls() != null) {
new ClassGen(f.getCls(), this, fallback).addClassBody(code);
}
if (it.hasNext()) {
code.add(',');
} }
code.add(')');
} }
if (enumFields.getFields().isEmpty()) { if (f.getCls() != null) {
code.startLine(); new ClassGen(f.getCls(), this, fallback).addClassBody(code);
} }
code.add(';'); if (it.hasNext()) {
code.newLine(); code.add(',');
}
}
if (enumFields.getFields().isEmpty()) {
code.startLine();
} }
code.add(';');
code.newLine();
} }
public String useClass(ArgType clsType) { public void useType(CodeWriter code, ArgType type) {
if (clsType.isGenericType()) { final PrimitiveType stype = type.getPrimitiveType();
return clsType.getObject(); if (stype == null) {
code.add(type.toString());
} else if (stype == PrimitiveType.OBJECT) {
if (type.isGenericType()) {
code.add(type.getObject());
} else {
useClass(code, ClassInfo.fromType(type));
}
} else if (stype == PrimitiveType.ARRAY) {
useType(code, type.getArrayElement());
code.add("[]");
} else {
code.add(stype.getLongName());
} }
return useClass(ClassInfo.fromType(clsType));
} }
public String useClass(ClassInfo classInfo) { public void useClass(CodeWriter code, ClassInfo classInfo) {
String baseClass = useClassInternal(cls.getClassInfo(), classInfo); ClassNode classNode = cls.dex().resolveClass(classInfo);
ArgType type = classInfo.getType(); if (classNode != null) {
ArgType[] generics = type.getGenericTypes(); code.attachAnnotation(classNode);
if (generics == null) {
return baseClass;
} }
String baseClass = useClassInternal(cls.getClassInfo(), classInfo);
StringBuilder sb = new StringBuilder(); ArgType[] generics = classInfo.getType().getGenericTypes();
sb.append(baseClass); code.add(baseClass);
sb.append('<'); if (generics != null) {
int len = generics.length; code.add('<');
for (int i = 0; i < len; i++) { int len = generics.length;
if (i != 0) { for (int i = 0; i < len; i++) {
sb.append(", "); if (i != 0) {
} code.add(", ");
ArgType gt = generics[i]; }
ArgType wt = gt.getWildcardType(); ArgType gt = generics[i];
if (wt != null) { ArgType wt = gt.getWildcardType();
sb.append('?'); if (wt != null) {
int bounds = gt.getWildcardBounds(); code.add('?');
if (bounds != 0) { int bounds = gt.getWildcardBounds();
sb.append(bounds == -1 ? " super " : " extends "); if (bounds != 0) {
sb.append(TypeGen.translate(this, wt)); code.add(bounds == -1 ? " super " : " extends ");
useType(code, wt);
}
} else {
useType(code, gt);
} }
} else {
sb.append(TypeGen.translate(this, gt));
} }
code.add('>');
} }
sb.append('>');
return sb.toString();
} }
private String useClassInternal(ClassInfo useCls, ClassInfo classInfo) { private String useClassInternal(ClassInfo useCls, ClassInfo classInfo) {
......
...@@ -99,7 +99,7 @@ public class InsnGen { ...@@ -99,7 +99,7 @@ public class InsnGen {
} else if (arg.isField()) { } else if (arg.isField()) {
FieldArg f = (FieldArg) arg; FieldArg f = (FieldArg) arg;
if (f.isStatic()) { if (f.isStatic()) {
code.add(staticField(f.getField())); staticField(code, f.getField());
} else { } else {
instanceField(code, f.getField(), f.getRegisterArg()); instanceField(code, f.getField(), f.getRegisterArg());
} }
...@@ -118,7 +118,7 @@ public class InsnGen { ...@@ -118,7 +118,7 @@ public class InsnGen {
} }
public void declareVar(CodeWriter code, RegisterArg arg) { public void declareVar(CodeWriter code, RegisterArg arg) {
code.add(useType(arg.getType())); useType(code, arg.getType());
code.add(' '); code.add(' ');
code.add(mgen.assignArg(arg)); code.add(mgen.assignArg(arg));
} }
...@@ -134,38 +134,52 @@ public class InsnGen { ...@@ -134,38 +134,52 @@ public class InsnGen {
if (replace != null) { if (replace != null) {
FieldInfo info = replace.getFieldInfo(); FieldInfo info = replace.getFieldInfo();
if (replace.isOuterClass()) { if (replace.isOuterClass()) {
code.add(useClass(info.getDeclClass())).add(".this"); useClass(code, info.getDeclClass());
code.add(".this");
} }
return; return;
} }
} }
addArgDot(code, arg); addArgDot(code, arg);
fieldNode = mth.dex().resolveField(field);
if (fieldNode != null) {
code.attachAnnotation(fieldNode);
}
code.add(field.getName()); code.add(field.getName());
} }
public static String makeStaticFieldAccess(FieldInfo field, ClassGen clsGen) { public static void makeStaticFieldAccess(CodeWriter code, FieldInfo field, ClassGen clsGen) {
ClassInfo declClass = field.getDeclClass(); ClassInfo declClass = field.getDeclClass();
if (clsGen.getClassNode().getFullName().startsWith(declClass.getFullName())) { boolean fieldFromThisClass = clsGen.getClassNode().getFullName().startsWith(declClass.getFullName());
return field.getName(); if (!fieldFromThisClass) {
// Android specific resources class handler
ClassInfo parentClass = declClass.getParentClass();
if (parentClass != null && parentClass.getShortName().equals("R")) {
clsGen.useClass(code, parentClass);
code.add('.');
code.add(declClass.getShortName());
} else {
clsGen.useClass(code, declClass);
}
code.add('.');
} }
// Android specific resources class handler FieldNode fieldNode = clsGen.getClassNode().dex().resolveField(field);
ClassInfo parentClass = declClass.getParentClass(); if (fieldNode != null) {
if (parentClass != null && parentClass.getShortName().equals("R")) { code.attachAnnotation(fieldNode);
return clsGen.useClass(parentClass) + "." + declClass.getShortName() + "." + field.getName();
} }
return clsGen.useClass(declClass) + '.' + field.getName(); code.add(field.getName());
} }
protected String staticField(FieldInfo field) { protected void staticField(CodeWriter code, FieldInfo field) {
return makeStaticFieldAccess(field, mgen.getClassGen()); makeStaticFieldAccess(code, field, mgen.getClassGen());
} }
public String useClass(ClassInfo cls) { public void useClass(CodeWriter code, ClassInfo cls) {
return mgen.getClassGen().useClass(cls); mgen.getClassGen().useClass(code, cls);
} }
private String useType(ArgType type) { private void useType(CodeWriter code, ArgType type) {
return TypeGen.translate(mgen.getClassGen(), type); mgen.getClassGen().useType(code, type);
} }
public boolean makeInsn(InsnNode insn, CodeWriter code) throws CodegenException { public boolean makeInsn(InsnNode insn, CodeWriter code) throws CodegenException {
...@@ -208,7 +222,8 @@ public class InsnGen { ...@@ -208,7 +222,8 @@ public class InsnGen {
case CONST_CLASS: case CONST_CLASS:
ArgType clsType = ((ConstClassNode) insn).getClsType(); ArgType clsType = ((ConstClassNode) insn).getClsType();
code.add(useType(clsType)).add(".class"); useType(code, clsType);
code.add(".class");
break; break;
case CONST: case CONST:
...@@ -227,7 +242,7 @@ public class InsnGen { ...@@ -227,7 +242,7 @@ public class InsnGen {
code.add('('); code.add('(');
} }
code.add('('); code.add('(');
code.add(useType((ArgType) ((IndexInsnNode) insn).getIndex())); useType(code, (ArgType) ((IndexInsnNode) insn).getIndex());
code.add(") "); code.add(") ");
addArg(code, insn.getArg(0), true); addArg(code, insn.getArg(0), true);
if (wrap) { if (wrap) {
...@@ -299,7 +314,7 @@ public class InsnGen { ...@@ -299,7 +314,7 @@ public class InsnGen {
} }
addArg(code, insn.getArg(0)); addArg(code, insn.getArg(0));
code.add(" instanceof "); code.add(" instanceof ");
code.add(useType((ArgType) ((IndexInsnNode) insn).getIndex())); useType(code, (ArgType) ((IndexInsnNode) insn).getIndex());
if (wrap) { if (wrap) {
code.add(')'); code.add(')');
} }
...@@ -315,7 +330,8 @@ public class InsnGen { ...@@ -315,7 +330,8 @@ public class InsnGen {
case NEW_ARRAY: { case NEW_ARRAY: {
ArgType arrayType = insn.getResult().getType(); ArgType arrayType = insn.getResult().getType();
code.add("new ").add(useType(arrayType.getArrayRootElement())); code.add("new ");
useType(code, arrayType.getArrayRootElement());
code.add('['); code.add('[');
addArg(code, insn.getArg(0)); addArg(code, insn.getArg(0));
code.add(']'); code.add(']');
...@@ -368,11 +384,12 @@ public class InsnGen { ...@@ -368,11 +384,12 @@ public class InsnGen {
} }
case SGET: case SGET:
code.add(staticField((FieldInfo) ((IndexInsnNode) insn).getIndex())); staticField(code, (FieldInfo) ((IndexInsnNode) insn).getIndex());
break; break;
case SPUT: case SPUT:
FieldInfo field = (FieldInfo) ((IndexInsnNode) insn).getIndex(); FieldInfo field = (FieldInfo) ((IndexInsnNode) insn).getIndex();
code.add(staticField(field)).add(" = "); staticField(code, field);
code.add(" = ");
addArg(code, insn.getArg(0), false); addArg(code, insn.getArg(0), false);
break; break;
...@@ -474,7 +491,8 @@ public class InsnGen { ...@@ -474,7 +491,8 @@ public class InsnGen {
private void filledNewArray(InsnNode insn, CodeWriter code) throws CodegenException { private void filledNewArray(InsnNode insn, CodeWriter code) throws CodegenException {
int c = insn.getArgsCount(); int c = insn.getArgsCount();
code.add("new ").add(useType(insn.getResult().getType())); code.add("new ");
useType(code, insn.getResult().getType());
code.add('{'); code.add('{');
for (int i = 0; i < c; i++) { for (int i = 0; i < c; i++) {
addArg(code, insn.getArg(i)); addArg(code, insn.getArg(i));
...@@ -539,7 +557,9 @@ public class InsnGen { ...@@ -539,7 +557,9 @@ public class InsnGen {
} }
int len = str.length(); int len = str.length();
str.delete(len - 2, len); str.delete(len - 2, len);
code.add("new ").add(useType(elType)).add("[]{").add(str.toString()).add('}'); code.add("new ");
useType(code, elType);
code.add("[]{").add(str.toString()).add('}');
} }
private void makeConstructor(ConstructorInsn insn, CodeWriter code) private void makeConstructor(ConstructorInsn insn, CodeWriter code)
...@@ -562,7 +582,13 @@ public class InsnGen { ...@@ -562,7 +582,13 @@ public class InsnGen {
defCtr.add(AFlag.DONT_GENERATE); defCtr.add(AFlag.DONT_GENERATE);
} }
} }
code.add("new ").add(parent == null ? "Object" : useClass(parent)).add("() "); code.add("new ");
if (parent == null) {
code.add("Object");
} else {
useClass(code, parent);
}
code.add("() ");
new ClassGen(cls, mgen.getClassGen().getParentGen(), fallback).addClassBody(code); new ClassGen(cls, mgen.getClassGen().getParentGen(), fallback).addClassBody(code);
return; return;
} }
...@@ -574,7 +600,8 @@ public class InsnGen { ...@@ -574,7 +600,8 @@ public class InsnGen {
} else if (insn.isThis()) { } else if (insn.isThis()) {
code.add("this"); code.add("this");
} else { } else {
code.add("new ").add(useClass(insn.getClassType())); code.add("new ");
useClass(code, insn.getClassType());
} }
generateArguments(code, insn, 0, mth.dex().resolveMethod(insn.getCallMth())); generateArguments(code, insn, 0, mth.dex().resolveMethod(insn.getCallMth()));
} }
...@@ -612,7 +639,8 @@ public class InsnGen { ...@@ -612,7 +639,8 @@ public class InsnGen {
ClassInfo insnCls = mth.getParentClass().getClassInfo(); ClassInfo insnCls = mth.getParentClass().getClassInfo();
ClassInfo declClass = callMth.getDeclClass(); ClassInfo declClass = callMth.getDeclClass();
if (!insnCls.equals(declClass)) { if (!insnCls.equals(declClass)) {
code.add(useClass(declClass)).add('.'); useClass(code, declClass);
code.add('.');
} }
break; break;
} }
...@@ -637,7 +665,9 @@ public class InsnGen { ...@@ -637,7 +665,9 @@ public class InsnGen {
InsnArg arg = insn.getArg(i); InsnArg arg = insn.getArg(i);
ArgType origType = originalType.get(origPos); ArgType origType = originalType.get(origPos);
if (!arg.getType().equals(origType)) { if (!arg.getType().equals(origType)) {
code.add('(').add(useType(origType)).add(')'); code.add('(');
useType(code, origType);
code.add(')');
addArg(code, arg, true); addArg(code, arg, true);
} else { } else {
addArg(code, arg, false); addArg(code, arg, false);
......
...@@ -93,7 +93,7 @@ public class MethodGen { ...@@ -93,7 +93,7 @@ public class MethodGen {
if (mth.getAccessFlags().isConstructor()) { if (mth.getAccessFlags().isConstructor()) {
code.add(classGen.getClassNode().getShortName()); // constructor code.add(classGen.getClassNode().getShortName()); // constructor
} else { } else {
code.add(TypeGen.translate(classGen, mth.getReturnType())); classGen.useType(code, mth.getReturnType());
code.add(' '); code.add(' ');
code.add(mth.getName()); code.add(mth.getName());
} }
...@@ -138,14 +138,14 @@ public class MethodGen { ...@@ -138,14 +138,14 @@ public class MethodGen {
ArgType type = arg.getType(); ArgType type = arg.getType();
if (type.isArray()) { if (type.isArray()) {
ArgType elType = type.getArrayElement(); ArgType elType = type.getArrayElement();
argsCode.add(TypeGen.translate(classGen, elType)); classGen.useType(argsCode, elType);
argsCode.add(" ..."); argsCode.add(" ...");
} else { } else {
LOG.warn(ErrorsCounter.formatErrorMsg(mth, "Last argument in varargs method not array")); LOG.warn(ErrorsCounter.formatErrorMsg(mth, "Last argument in varargs method not array"));
argsCode.add(TypeGen.translate(classGen, arg.getType())); classGen.useType(argsCode, arg.getType());
} }
} else { } else {
argsCode.add(TypeGen.translate(classGen, arg.getType())); classGen.useType(argsCode, arg.getType());
} }
argsCode.add(' '); argsCode.add(' ');
argsCode.add(makeArgName(arg)); argsCode.add(makeArgName(arg));
...@@ -181,7 +181,8 @@ public class MethodGen { ...@@ -181,7 +181,8 @@ public class MethodGen {
if (type.isPrimitive()) { if (type.isPrimitive()) {
return base + type.getPrimitiveType().getShortName().toLowerCase(); return base + type.getPrimitiveType().getShortName().toLowerCase();
} else { } else {
return base + "_" + Utils.escape(TypeGen.translate(classGen, arg.getType())); // TODO: prettify variable name
return base + "_" + Utils.escape(type.toString());
} }
} }
} }
......
...@@ -209,7 +209,7 @@ public class RegionGen extends InsnGen { ...@@ -209,7 +209,7 @@ public class RegionGen extends InsnGen {
for (Object k : keys) { for (Object k : keys) {
code.startLine("case "); code.startLine("case ");
if (k instanceof IndexInsnNode) { if (k instanceof IndexInsnNode) {
code.add(staticField((FieldInfo) ((IndexInsnNode) k).getIndex())); staticField(code, (FieldInfo) ((IndexInsnNode) k).getIndex());
} else { } else {
code.add(TypeGen.literalToString((Integer) k, arg.getType())); code.add(TypeGen.literalToString((Integer) k, arg.getType()));
} }
...@@ -270,7 +270,11 @@ public class RegionGen extends InsnGen { ...@@ -270,7 +270,11 @@ public class RegionGen extends InsnGen {
IContainer region = handler.getHandlerRegion(); IContainer region = handler.getHandlerRegion();
if (region != null) { if (region != null) {
code.startLine("} catch ("); code.startLine("} catch (");
code.add(handler.isCatchAll() ? "Throwable" : useClass(handler.getCatchType())); if (handler.isCatchAll()) {
code.add("Throwable");
} else {
useClass(code, handler.getCatchType());
}
code.add(' '); code.add(' ');
code.add(mgen.assignNamedArg(handler.getArg())); code.add(mgen.assignNamedArg(handler.getArg()));
code.add(") {"); code.add(") {");
......
...@@ -8,20 +8,6 @@ import jadx.core.utils.exceptions.JadxRuntimeException; ...@@ -8,20 +8,6 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
public class TypeGen { public class TypeGen {
public static String translate(ClassGen clsGen, ArgType type) {
final PrimitiveType stype = type.getPrimitiveType();
if (stype == null) {
return type.toString();
}
if (stype == PrimitiveType.OBJECT) {
return clsGen.useClass(type);
}
if (stype == PrimitiveType.ARRAY) {
return translate(clsGen, type.getArrayElement()) + "[]";
}
return stype.getLongName();
}
public static String signature(ArgType type) { public static String signature(ArgType type) {
final PrimitiveType stype = type.getPrimitiveType(); final PrimitiveType stype = type.getPrimitiveType();
if (stype == PrimitiveType.OBJECT) { if (stype == PrimitiveType.OBJECT) {
......
...@@ -57,7 +57,6 @@ final class LocalVar extends RegisterArg { ...@@ -57,7 +57,6 @@ final class LocalVar extends RegisterArg {
} else if (el.isGenericType()) { } else if (el.isGenericType()) {
apply = true; apply = true;
} else { } else {
LOG.debug("Local var signature from debug info not generic: {}, parsed: {}", sign, gType);
apply = false; apply = false;
} }
return apply; return apply;
......
...@@ -36,7 +36,6 @@ public class JadxWrapper { ...@@ -36,7 +36,6 @@ public class JadxWrapper {
} }
} }
public void saveAll(final File dir, final ProgressMonitor progressMonitor) { public void saveAll(final File dir, final ProgressMonitor progressMonitor) {
Runnable save = new Runnable() { Runnable save = new Runnable() {
@Override @Override
......
...@@ -106,6 +106,10 @@ public class JClass extends JNode { ...@@ -106,6 +106,10 @@ public class JClass extends JNode {
return jParent.getRootClass(); return jParent.getRootClass();
} }
public String getFullName() {
return cls.getFullName();
}
@Override @Override
public int getLine() { public int getLine() {
return cls.getDecompiledLine(); return cls.getDecompiledLine();
......
...@@ -2,6 +2,7 @@ package jadx.gui.ui; ...@@ -2,6 +2,7 @@ package jadx.gui.ui;
import jadx.api.CodePosition; import jadx.api.CodePosition;
import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JClass;
import jadx.gui.utils.Position;
import javax.swing.JViewport; import javax.swing.JViewport;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
...@@ -64,7 +65,7 @@ class CodeArea extends RSyntaxTextArea { ...@@ -64,7 +65,7 @@ class CodeArea extends RSyntaxTextArea {
private boolean isJumpToken(Token token) { private boolean isJumpToken(Token token) {
if (token.getType() == TokenTypes.IDENTIFIER) { if (token.getType() == TokenTypes.IDENTIFIER) {
CodePosition pos = getCodePosition(cls, this, token.getOffset()); Position pos = getPosition(cls, this, token.getOffset());
if (pos != null) { if (pos != null) {
return true; return true;
} }
...@@ -80,15 +81,22 @@ class CodeArea extends RSyntaxTextArea { ...@@ -80,15 +81,22 @@ class CodeArea extends RSyntaxTextArea {
return super.getUnderlineForToken(t); return super.getUnderlineForToken(t);
} }
static CodePosition getCodePosition(JClass jCls, RSyntaxTextArea textArea, int offset) { static Position getPosition(JClass jCls, RSyntaxTextArea textArea, int offset) {
try { try {
int line = textArea.getLineOfOffset(offset); int line = textArea.getLineOfOffset(offset);
int lineOffset = offset - textArea.getLineStartOffset(line); int lineOffset = offset - textArea.getLineStartOffset(line);
return jCls.getCls().getDefinitionPosition(line + 1, lineOffset + 1); CodePosition pos = jCls.getCls().getDefinitionPosition(line + 1, lineOffset + 1);
if (pos != null && pos.isSet()) {
return new Position(pos);
}
} catch (BadLocationException e) { } catch (BadLocationException e) {
LOG.error("Can't get line by offset", e); LOG.error("Can't get line by offset", e);
return null;
} }
return null;
}
Position getCurrentPosition() {
return new Position(cls, getCaretLineNumber());
} }
void scrollToLine(int line) { void scrollToLine(int line) {
...@@ -145,14 +153,14 @@ class CodeArea extends RSyntaxTextArea { ...@@ -145,14 +153,14 @@ class CodeArea extends RSyntaxTextArea {
if (token != null) { if (token != null) {
offset = token.getOffset(); offset = token.getOffset();
} }
final CodePosition defPos = getCodePosition(jCls, textArea, offset); final Position defPos = getPosition(jCls, textArea, offset);
if (defPos != null) { if (defPos != null) {
final int sourceOffset = offset; final int sourceOffset = offset;
return new LinkGeneratorResult() { return new LinkGeneratorResult() {
@Override @Override
public HyperlinkEvent execute() { public HyperlinkEvent execute() {
return new HyperlinkEvent(defPos, HyperlinkEvent.EventType.ACTIVATED, null, return new HyperlinkEvent(defPos, HyperlinkEvent.EventType.ACTIVATED, null,
defPos.getJavaClass().getFullName()); defPos.getCls().getFullName());
} }
@Override @Override
...@@ -170,11 +178,13 @@ class CodeArea extends RSyntaxTextArea { ...@@ -170,11 +178,13 @@ class CodeArea extends RSyntaxTextArea {
@Override @Override
public void hyperlinkUpdate(HyperlinkEvent e) { public void hyperlinkUpdate(HyperlinkEvent e) {
Object obj = e.getSource(); Object obj = e.getSource();
if (obj instanceof CodePosition) { if (obj instanceof Position) {
CodePosition pos = (CodePosition) obj; Position pos = (Position) obj;
JClass cls = new JClass(pos.getJavaClass());
codePanel.getCodePanel().showCode(cls, pos.getLine());
LOG.debug("Code jump to: {}", pos); LOG.debug("Code jump to: {}", pos);
TabbedPane tabbedPane = codePanel.getTabbedPane();
tabbedPane.getJumpManager().addPosition(getCurrentPosition());
tabbedPane.getJumpManager().addPosition(pos);
tabbedPane.showCode(pos);
} }
} }
} }
......
...@@ -17,14 +17,14 @@ class CodePanel extends JPanel { ...@@ -17,14 +17,14 @@ class CodePanel extends JPanel {
private static final long serialVersionUID = 5310536092010045565L; private static final long serialVersionUID = 5310536092010045565L;
private final TabbedPane codePanel; private final TabbedPane tabbedPane;
private final JClass jClass; private final JClass jClass;
private final SearchBar searchBar; private final SearchBar searchBar;
private final CodeArea codeArea; private final CodeArea codeArea;
private final RTextScrollPane scrollPane; private final RTextScrollPane scrollPane;
CodePanel(TabbedPane panel, JClass cls) { CodePanel(TabbedPane panel, JClass cls) {
codePanel = panel; tabbedPane = panel;
jClass = cls; jClass = cls;
codeArea = new CodeArea(this); codeArea = new CodeArea(this);
searchBar = new SearchBar(codeArea); searchBar = new SearchBar(codeArea);
...@@ -49,8 +49,8 @@ class CodePanel extends JPanel { ...@@ -49,8 +49,8 @@ class CodePanel extends JPanel {
} }
} }
TabbedPane getCodePanel() { TabbedPane getTabbedPane() {
return codePanel; return tabbedPane;
} }
JClass getCls() { JClass getCls() {
......
...@@ -61,6 +61,9 @@ public class MainWindow extends JFrame { ...@@ -61,6 +61,9 @@ public class MainWindow extends JFrame {
private static final ImageIcon ICON_FLAT_PKG = Utils.openIcon("empty_logical_package_obj"); private static final ImageIcon ICON_FLAT_PKG = Utils.openIcon("empty_logical_package_obj");
private static final ImageIcon ICON_SEARCH = Utils.openIcon("magnifier"); private static final ImageIcon ICON_SEARCH = Utils.openIcon("magnifier");
private static final ImageIcon ICON_BACK = Utils.openIcon("icon_back");
private static final ImageIcon ICON_FORWARD = Utils.openIcon("icon_forward");
private final JadxWrapper wrapper; private final JadxWrapper wrapper;
private JPanel mainPanel; private JPanel mainPanel;
...@@ -219,6 +222,26 @@ public class MainWindow extends JFrame { ...@@ -219,6 +222,26 @@ public class MainWindow extends JFrame {
toolbar.addSeparator(); toolbar.addSeparator();
final JButton backButton = new JButton(ICON_BACK);
backButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tabbedPane.navBack();
}
});
backButton.setToolTipText(NLS.str("nav.back"));
toolbar.add(backButton);
final JButton forwardButton = new JButton(ICON_FORWARD);
forwardButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tabbedPane.navForward();
}
});
forwardButton.setToolTipText(NLS.str("nav.forward"));
toolbar.add(forwardButton);
mainPanel.add(toolbar, BorderLayout.NORTH); mainPanel.add(toolbar, BorderLayout.NORTH);
} }
......
package jadx.gui.ui; package jadx.gui.ui;
import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JClass;
import jadx.gui.utils.JumpManager;
import jadx.gui.utils.NLS; import jadx.gui.utils.NLS;
import jadx.gui.utils.Position;
import jadx.gui.utils.Utils; import jadx.gui.utils.Utils;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
...@@ -36,6 +38,7 @@ class TabbedPane extends JTabbedPane { ...@@ -36,6 +38,7 @@ class TabbedPane extends JTabbedPane {
private final MainWindow mainWindow; private final MainWindow mainWindow;
private final Map<JClass, CodePanel> openTabs = new LinkedHashMap<JClass, CodePanel>(); private final Map<JClass, CodePanel> openTabs = new LinkedHashMap<JClass, CodePanel>();
private JumpManager jumps = new JumpManager();
TabbedPane(MainWindow window) { TabbedPane(MainWindow window) {
mainWindow = window; mainWindow = window;
...@@ -63,18 +66,40 @@ class TabbedPane extends JTabbedPane { ...@@ -63,18 +66,40 @@ class TabbedPane extends JTabbedPane {
} }
void showCode(final JClass cls, final int line) { void showCode(final JClass cls, final int line) {
final CodePanel codePanel = getCodePanel(cls); showCode(new Position(cls, line));
}
void showCode(final Position pos) {
final CodePanel codePanel = getCodePanel(pos.getCls());
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@Override @Override
public void run() { public void run() {
setSelectedComponent(codePanel); setSelectedComponent(codePanel);
CodeArea codeArea = codePanel.getCodeArea(); CodeArea codeArea = codePanel.getCodeArea();
codeArea.scrollToLine(line); codeArea.scrollToLine(pos.getLine());
codeArea.requestFocus(); codeArea.requestFocus();
} }
}); });
} }
public void navBack() {
Position pos = jumps.getPrev();
if (pos != null) {
showCode(pos);
}
}
public void navForward() {
Position pos = jumps.getNext();
if (pos != null) {
showCode(pos);
}
}
public JumpManager getJumpManager() {
return jumps;
}
private void addCodePanel(CodePanel codePanel) { private void addCodePanel(CodePanel codePanel) {
openTabs.put(codePanel.getCls(), codePanel); openTabs.put(codePanel.getCls(), codePanel);
add(codePanel); add(codePanel);
......
package jadx.gui.utils;
import java.util.ArrayList;
import java.util.List;
public class JumpManager {
private List<Position> list = new ArrayList<Position>();
private int currentPos = 0;
public void addPosition(Position pos) {
if (pos.equals(getCurrent())) {
return;
}
currentPos++;
if (currentPos >= list.size()) {
list.add(pos);
currentPos = list.size() - 1;
} else {
list.set(currentPos, pos);
int size = list.size();
for (int i = currentPos + 1; i < size; i++) {
list.set(i, null);
}
}
}
private Position getCurrent() {
if (currentPos < list.size()) {
return list.get(currentPos);
}
return null;
}
public Position getPrev() {
if (currentPos == 0) {
return null;
}
currentPos--;
return list.get(currentPos);
}
public Position getNext() {
int newPos = currentPos + 1;
if (newPos >= list.size()) {
currentPos = list.size() - 1;
return null;
}
Position position = list.get(newPos);
if (position == null) {
return null;
}
currentPos = newPos;
return position;
}
}
package jadx.gui.utils;
import jadx.api.CodePosition;
import jadx.gui.treemodel.JClass;
public class Position {
private final JClass cls;
private final int line;
public Position(CodePosition pos) {
this.cls = new JClass(pos.getJavaClass());
this.line = pos.getLine();
}
public Position(JClass cls, int line) {
this.cls = cls;
this.line = line;
}
public JClass getCls() {
return cls;
}
public int getLine() {
return line;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Position)) {
return false;
}
Position position = (Position) obj;
return line == position.line && cls.equals(position.cls);
}
@Override
public int hashCode() {
return 31 * cls.hashCode() + line;
}
@Override
public String toString() {
return "Position: " + cls + " : " + line;
}
}
...@@ -20,3 +20,6 @@ search.find=Find ...@@ -20,3 +20,6 @@ search.find=Find
tabs.close=Close tabs.close=Close
tabs.closeOthers=Close Others tabs.closeOthers=Close Others
tabs.closeAll=Close All tabs.closeAll=Close All
nav.back=Back
nav.forward=Forward
package jadx.gui.tests
import jadx.gui.utils.JumpManager
import jadx.gui.utils.Position
import spock.lang.Specification
class TestJumpManager extends Specification {
JumpManager jm
def setup() {
jm = new JumpManager()
}
def "empty history"() {
expect:
jm.getPrev() == null
jm.getNext() == null
}
def "1 element"() {
when:
jm.addPosition(Mock(Position))
then:
jm.getPrev() == null
jm.getNext() == null
}
def "2 elements"() {
when:
def mock1 = Mock(Position)
jm.addPosition(mock1)
def mock2 = Mock(Position)
jm.addPosition(mock2)
// 1 - 2@
then:
noExceptionThrown()
jm.getPrev() == mock1
jm.getNext() == mock2
jm.getNext() == null
}
def "navigation"() {
expect:
def mock1 = Mock(Position)
jm.addPosition(mock1)
// 1@
def mock2 = Mock(Position)
jm.addPosition(mock2)
// 1 - 2@
jm.getPrev() == mock1
// 1@ - 2
def mock3 = Mock(Position)
jm.addPosition(mock3)
// 1 - 3@
jm.getNext() == null
jm.getPrev() == mock1
// 1@ - 3
jm.getNext() == mock3
}
def "navigation2"() {
expect:
def mock1 = Mock(Position)
jm.addPosition(mock1)
// 1@
def mock2 = Mock(Position)
jm.addPosition(mock2)
// 1 - 2@
def mock3 = Mock(Position)
jm.addPosition(mock3)
// 1 - 2 - 3@
def mock4 = Mock(Position)
jm.addPosition(mock4)
// 1 - 2 - 3 - 4@
jm.getPrev() == mock3
// 1 - 2 - 3@ - 4
jm.getPrev() == mock2
// 1 - 2@ - 3 - 4
def mock5 = Mock(Position)
jm.addPosition(mock5)
// 1 - 2 - 5@
jm.getNext() == null
jm.getNext() == null
jm.getPrev() == mock2
// 1 - 2@ - 5
jm.getPrev() == mock1
// 1@ - 2 - 5
jm.getPrev() == null
jm.getNext() == mock2
// 1 - 2@ - 5
jm.getNext() == mock5
// 1 - 2 - 5@
jm.getNext() == null
}
def "add same element"() {
when:
def mock = Mock(Position)
jm.addPosition(mock)
jm.addPosition(mock)
then:
noExceptionThrown()
jm.getPrev() == null
jm.getNext() == null
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment