未验证 提交 4557d052 编写于 作者: S Skylot

fix: use correct type for anonymous class instance (#597)

上级 fa421d16
......@@ -717,14 +717,7 @@ public class InsnGen {
throw new CodegenException("Anonymous inner class unlimited recursion detected."
+ " Convert class to inner: " + cls.getClassInfo().getFullName());
}
cls.add(AFlag.DONT_GENERATE);
ArgType parent;
if (cls.getInterfaces().size() == 1) {
parent = cls.getInterfaces().get(0);
} else {
parent = cls.getSuperClass();
}
ArgType parent = cls.get(AType.ANONYMOUS_CLASS_BASE).getBaseType();
// hide empty anonymous constructors
for (MethodNode ctor : cls.getMethods()) {
if (ctor.contains(AFlag.ANONYMOUS_CONSTRUCTOR)
......@@ -732,13 +725,8 @@ public class InsnGen {
ctor.add(AFlag.DONT_GENERATE);
}
}
code.add("new ");
if (parent == null) {
code.add("Object");
} else {
useClass(code, parent);
}
useClass(code, parent);
MethodNode callMth = mth.root().resolveMethod(insn.getCallMth());
generateMethodArguments(code, insn, 0, callMth);
code.add(' ');
......
......@@ -162,8 +162,7 @@ public class NameGen {
InsnNode assignInsn = assignArg.getParentInsn();
if (assignInsn != null) {
String name = makeNameFromInsn(assignInsn);
if (name != null && !NameMapper.isReserved(name)) {
assignArg.setName(name);
if (name != null && NameMapper.isValidAndPrintable(name)) {
return name;
}
}
......@@ -202,7 +201,11 @@ public class NameGen {
return vName;
}
if (shortName != null) {
return StringUtils.escape(shortName.toLowerCase());
String lower = StringUtils.escape(shortName.toLowerCase());
if (shortName.equals(lower)) {
return lower + "Var";
}
return lower;
}
}
return StringUtils.escape(type.toString());
......
......@@ -2,6 +2,7 @@ package jadx.core.dex.attributes;
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
import jadx.core.dex.attributes.nodes.AnonymousClassBaseAttr;
import jadx.core.dex.attributes.nodes.ClassTypeVarsAttr;
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
import jadx.core.dex.attributes.nodes.EdgeInsnAttr;
......@@ -16,6 +17,7 @@ import jadx.core.dex.attributes.nodes.JumpInfo;
import jadx.core.dex.attributes.nodes.LocalVarsDebugInfoAttr;
import jadx.core.dex.attributes.nodes.LoopInfo;
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
import jadx.core.dex.attributes.nodes.MethodBridgeAttr;
import jadx.core.dex.attributes.nodes.MethodInlineAttr;
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
import jadx.core.dex.attributes.nodes.MethodTypeVarsAttr;
......@@ -51,6 +53,7 @@ public final class AType<T extends IJadxAttribute> implements IJadxAttrType<T> {
public static final AType<EnumClassAttr> ENUM_CLASS = new AType<>();
public static final AType<EnumMapAttr> ENUM_MAP = new AType<>();
public static final AType<ClassTypeVarsAttr> CLASS_TYPE_VARS = new AType<>();
public static final AType<AnonymousClassBaseAttr> ANONYMOUS_CLASS_BASE = new AType<>();
// field
public static final AType<FieldInitInsnAttr> FIELD_INIT_INSN = new AType<>();
......@@ -63,6 +66,7 @@ public final class AType<T extends IJadxAttribute> implements IJadxAttrType<T> {
public static final AType<MethodOverrideAttr> METHOD_OVERRIDE = new AType<>();
public static final AType<MethodTypeVarsAttr> METHOD_TYPE_VARS = new AType<>();
public static final AType<AttrList<TryCatchBlockAttr>> TRY_BLOCKS_LIST = new AType<>();
public static final AType<MethodBridgeAttr> BRIDGED_BY = new AType<>();
// region
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<>();
......
package jadx.core.dex.attributes.nodes;
import jadx.api.plugins.input.data.attributes.PinnedAttribute;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.instructions.args.ArgType;
public class AnonymousClassBaseAttr extends PinnedAttribute {
private final ArgType baseType;
public AnonymousClassBaseAttr(ArgType baseType) {
this.baseType = baseType;
}
public ArgType getBaseType() {
return baseType;
}
@Override
public AType<AnonymousClassBaseAttr> getAttrType() {
return AType.ANONYMOUS_CLASS_BASE;
}
@Override
public String toString() {
return "AnonymousClassBaseAttr{" + baseType + '}';
}
}
package jadx.core.dex.attributes.nodes;
import jadx.api.plugins.input.data.attributes.PinnedAttribute;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.nodes.MethodNode;
public class MethodBridgeAttr extends PinnedAttribute {
private final MethodNode bridgeMth;
public MethodBridgeAttr(MethodNode bridgeMth) {
this.bridgeMth = bridgeMth;
}
public MethodNode getBridgeMth() {
return bridgeMth;
}
@Override
public AType<MethodBridgeAttr> getAttrType() {
return AType.BRIDGED_BY;
}
@Override
public String toString() {
return "BRIDGED_BY: " + bridgeMth;
}
}
......@@ -3,10 +3,13 @@ package jadx.core.dex.attributes.nodes;
import java.util.List;
import java.util.SortedSet;
import org.jetbrains.annotations.Nullable;
import jadx.api.plugins.input.data.attributes.PinnedAttribute;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.nodes.IMethodDetails;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.Utils;
public class MethodOverrideAttr extends PinnedAttribute {
......@@ -29,6 +32,11 @@ public class MethodOverrideAttr extends PinnedAttribute {
return overrideList.isEmpty();
}
@Nullable
public IMethodDetails getBaseMth() {
return Utils.last(overrideList);
}
public List<IMethodDetails> getOverrideList() {
return overrideList;
}
......
......@@ -8,6 +8,9 @@ import org.jetbrains.annotations.Nullable;
import jadx.core.clsp.ClspClass;
import jadx.core.clsp.ClspMethod;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.MethodBridgeAttr;
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.BaseInvokeNode;
import jadx.core.dex.instructions.args.ArgType;
......@@ -67,7 +70,7 @@ public class MethodUtils {
return null;
}
public boolean processMethodArgsOverloaded(ArgType startCls, MethodInfo mthInfo, @Nullable List<IMethodDetails> collectedMths) {
private boolean processMethodArgsOverloaded(ArgType startCls, MethodInfo mthInfo, @Nullable List<IMethodDetails> collectedMths) {
if (startCls == null || !startCls.isObject()) {
return false;
}
......@@ -122,4 +125,25 @@ public class MethodUtils {
}
return false;
}
@Nullable
public IMethodDetails getOverrideBaseMth(MethodNode mth) {
MethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE);
if (overrideAttr == null) {
return null;
}
return overrideAttr.getBaseMth();
}
public ClassInfo getMethodOriginDeclClass(MethodNode mth) {
IMethodDetails baseMth = getOverrideBaseMth(mth);
if (baseMth != null) {
return baseMth.getMethodInfo().getDeclClass();
}
MethodBridgeAttr bridgeAttr = mth.get(AType.BRIDGED_BY);
if (bridgeAttr != null) {
return getMethodOriginDeclClass(bridgeAttr.getBridgeMth());
}
return mth.getMethodInfo().getDeclClass();
}
}
......@@ -17,6 +17,7 @@ import jadx.core.clsp.ClspClass;
import jadx.core.clsp.ClspMethod;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.MethodBridgeAttr;
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
import jadx.core.dex.attributes.nodes.RenameReasonAttr;
import jadx.core.dex.info.AccessInfo;
......@@ -65,13 +66,13 @@ public class OverrideMethodVisitor extends AbstractVisitor {
MethodOverrideAttr attr = processOverrideMethods(cls, mth, superTypes);
if (attr != null) {
mth.addAttr(attr);
IMethodDetails baseMth = Utils.last(attr.getOverrideList());
IMethodDetails baseMth = attr.getBaseMth();
if (baseMth != null) {
boolean updated = fixMethodReturnType(mth, baseMth, superTypes);
updated |= fixMethodArgTypes(mth, baseMth, superTypes);
if (updated && cls.root().getArgs().isRenameValid()) {
if (updated) {
// check if new signature cause method collisions
fixMethodSignatureCollisions(mth);
checkMethodSignatureCollisions(mth, cls.root().getArgs().isRenameValid());
}
}
}
......@@ -343,7 +344,7 @@ public class OverrideMethodVisitor extends AbstractVisitor {
return null;
}
private void fixMethodSignatureCollisions(MethodNode mth) {
private void checkMethodSignatureCollisions(MethodNode mth, boolean rename) {
String mthName = mth.getMethodInfo().getAlias();
String newSignature = MethodInfo.makeShortId(mthName, mth.getArgTypes(), null);
for (MethodNode otherMth : mth.getParentClass().getMethods()) {
......@@ -351,12 +352,16 @@ public class OverrideMethodVisitor extends AbstractVisitor {
if (otherMthName.equals(mthName) && otherMth != mth) {
String otherSignature = otherMth.getMethodInfo().makeSignature(true, false);
if (otherSignature.equals(newSignature)) {
if (otherMth.contains(AFlag.DONT_RENAME) || otherMth.contains(AType.METHOD_OVERRIDE)) {
otherMth.addWarnComment("Can't rename method to resolve collision");
} else {
otherMth.getMethodInfo().setAlias(makeNewAlias(otherMth));
otherMth.addAttr(new RenameReasonAttr("avoid collision after fix types in other method"));
if (rename) {
if (otherMth.contains(AFlag.DONT_RENAME) || otherMth.contains(AType.METHOD_OVERRIDE)) {
otherMth.addWarnComment("Can't rename method to resolve collision");
} else {
otherMth.getMethodInfo().setAlias(makeNewAlias(otherMth));
otherMth.addAttr(new RenameReasonAttr("avoid collision after fix types in other method"));
}
}
otherMth.addAttr(new MethodBridgeAttr(mth));
return;
}
}
}
......
package jadx.core.dex.visitors;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.nodes.AnonymousClassBaseAttr;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
......@@ -39,6 +41,7 @@ public class ProcessAnonymous extends AbstractVisitor {
return;
}
cls.add(AFlag.ANONYMOUS_CLASS);
cls.addAttr(new AnonymousClassBaseAttr(getBaseType(cls)));
cls.add(AFlag.DONT_GENERATE);
for (MethodNode mth : cls.getMethods()) {
......@@ -49,6 +52,16 @@ public class ProcessAnonymous extends AbstractVisitor {
}
}
private static ArgType getBaseType(ClassNode cls) {
ArgType parent;
if (cls.getInterfaces().size() == 1) {
parent = cls.getInterfaces().get(0);
} else {
parent = cls.getSuperClass();
}
return parent != null ? parent : ArgType.OBJECT;
}
private static boolean isStaticFieldUsedOutside(ClassNode cls) {
ClassNode topCls = cls.getTopParentClass();
for (FieldNode field : cls.getFields()) {
......
......@@ -19,6 +19,7 @@ import jadx.core.Consts;
import jadx.core.clsp.ClspGraph;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.AnonymousClassBaseAttr;
import jadx.core.dex.attributes.nodes.PhiListAttr;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.instructions.ArithNode;
......@@ -35,12 +36,15 @@ import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.instructions.mods.ConstructorInsn;
import jadx.core.dex.instructions.mods.TernaryInsn;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.IMethodDetails;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.nodes.utils.MethodUtils;
import jadx.core.dex.trycatch.ExcHandlerAttr;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.dex.visitors.AttachMethodDetails;
......@@ -273,6 +277,11 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
addBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, clsType));
break;
case CONSTRUCTOR:
ArgType ctrClsType = replaceAnonymousType((ConstructorInsn) insn);
addBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, ctrClsType));
break;
case CONST:
LiteralArg constLit = (LiteralArg) insn.getArg(0);
addBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, constLit.getType()));
......@@ -308,6 +317,19 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
}
}
private ArgType replaceAnonymousType(ConstructorInsn ctr) {
if (ctr.isNewInstance()) {
ClassNode ctrCls = root.resolveClass(ctr.getClassType());
if (ctrCls != null && ctrCls.contains(AFlag.DONT_GENERATE)) {
AnonymousClassBaseAttr baseTypeAttr = ctrCls.get(AType.ANONYMOUS_CLASS_BASE);
if (baseTypeAttr != null) {
return baseTypeAttr.getBaseType();
}
}
}
return ctr.getClassType().getType();
}
private ITypeBound makeAssignFieldGetBound(IndexInsnNode insn) {
ArgType initType = insn.getResult().getInitType();
if (initType.containsTypeVariable()) {
......@@ -340,7 +362,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
return null;
}
if (insn instanceof BaseInvokeNode) {
TypeBoundInvokeUse invokeUseBound = makeInvokeUseBound(regArg, (BaseInvokeNode) insn);
ITypeBound invokeUseBound = makeInvokeUseBound(regArg, (BaseInvokeNode) insn);
if (invokeUseBound != null) {
return invokeUseBound;
}
......@@ -352,21 +374,32 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
return new TypeBoundConst(BoundEnum.USE, regArg.getInitType(), regArg);
}
private TypeBoundInvokeUse makeInvokeUseBound(RegisterArg regArg, BaseInvokeNode invoke) {
private ITypeBound makeInvokeUseBound(RegisterArg regArg, BaseInvokeNode invoke) {
InsnArg instanceArg = invoke.getInstanceArg();
if (instanceArg == null || instanceArg == regArg) {
if (instanceArg == null) {
return null;
}
IMethodDetails methodDetails = root.getMethodUtils().getMethodDetails(invoke);
MethodUtils methodUtils = root.getMethodUtils();
IMethodDetails methodDetails = methodUtils.getMethodDetails(invoke);
if (methodDetails == null) {
return null;
}
int argIndex = invoke.getArgIndex(regArg) - invoke.getFirstArgOffset();
ArgType argType = methodDetails.getArgTypes().get(argIndex);
if (!argType.containsTypeVariable()) {
return null;
if (instanceArg != regArg) {
int argIndex = invoke.getArgIndex(regArg) - invoke.getFirstArgOffset();
ArgType argType = methodDetails.getArgTypes().get(argIndex);
if (!argType.containsTypeVariable()) {
return null;
}
return new TypeBoundInvokeUse(root, invoke, regArg, argType);
}
return new TypeBoundInvokeUse(root, invoke, regArg, argType);
// for override methods use origin declared class as type
if (methodDetails instanceof MethodNode) {
MethodNode callMth = (MethodNode) methodDetails;
ClassInfo declCls = methodUtils.getMethodOriginDeclClass(callMth);
return new TypeBoundConst(BoundEnum.USE, declCls.getType(), regArg);
}
return null;
}
private boolean tryPossibleTypes(MethodNode mth, SSAVar var, ArgType type) {
......
......@@ -2,9 +2,11 @@ package jadx.tests.integration.inner;
import org.junit.jupiter.api.Test;
import jadx.NotYetImplemented;
import jadx.api.CommentsLevel;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestAnonymousClass16 extends IntegrationTest {
public static class TestCls {
......@@ -26,9 +28,18 @@ public class TestAnonymousClass16 extends IntegrationTest {
}
@Test
@NotYetImplemented
public void test() {
getArgs().setCommentsLevel(CommentsLevel.NONE);
noDebugInfo();
getClassNode(TestCls.class);
assertThat(getClassNode(TestCls.class))
.code()
.doesNotContain("r0")
.doesNotContain("AnonymousClass1 r0 = ")
.containsLines(2,
"Something something = new Something() {",
indent() + "{",
indent(2) + "put(\"a\", \"b\");",
indent() + "}",
"};");
}
}
......@@ -3,6 +3,7 @@ package jadx.tests.integration.inner;
import org.junit.jupiter.api.Test;
import jadx.NotYetImplemented;
import jadx.api.CommentsLevel;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
......@@ -41,9 +42,11 @@ public class TestAnonymousClass3a extends IntegrationTest {
@Test
@NotYetImplemented
public void test() {
getArgs().setCommentsLevel(CommentsLevel.NONE);
assertThat(getClassNode(TestCls.class))
.code()
.doesNotContain("synthetic")
.doesNotContain("access$00")
.doesNotContain("AnonymousClass_")
.doesNotContain("unused = ")
.containsLine(4, "public void run() {")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册