From ae31fee8dd8d58e8fa007d0c201f11649966af5d Mon Sep 17 00:00:00 2001 From: Skylot Date: Fri, 29 May 2020 17:39:15 +0100 Subject: [PATCH] fix: add cast to exact type on field access (#729) --- .../core/dex/instructions/IndexInsnNode.java | 13 +++++--- .../java/jadx/core/dex/nodes/RootNode.java | 5 +++ .../jadx/core/dex/visitors/ModVisitor.java | 32 +++++++++++++++++++ .../integration/types/TestFieldAccess.java | 31 ++++++++++++++++++ 4 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/types/TestFieldAccess.java diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/IndexInsnNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/IndexInsnNode.java index 66cea5297..698f8e496 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/IndexInsnNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/IndexInsnNode.java @@ -41,10 +41,15 @@ public class IndexInsnNode extends InsnNode { switch (insnType) { case CAST: case CHECK_CAST: - return InsnUtils.formatOffset(offset) + ": " - + InsnUtils.insnTypeToString(insnType) - + getResult() + " = (" + InsnUtils.indexToString(index) + ") " - + Utils.listToString(getArguments()); + StringBuilder sb = new StringBuilder(); + sb.append(InsnUtils.formatOffset(offset)).append(": "); + sb.append(insnType).append(' '); + if (getResult() != null) { + sb.append(getResult()).append(" = "); + } + sb.append('(').append(InsnUtils.indexToString(index)).append(") "); + sb.append(Utils.listToString(getArguments())); + return sb.toString(); default: return super.toString() + ' ' + InsnUtils.indexToString(index); diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java index ee95b0297..84a63ef00 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java @@ -30,6 +30,7 @@ import jadx.core.dex.nodes.utils.MethodUtils; import jadx.core.dex.nodes.utils.TypeUtils; import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.IDexTreeVisitor; +import jadx.core.dex.visitors.typeinference.TypeCompare; import jadx.core.dex.visitors.typeinference.TypeUpdate; import jadx.core.utils.CacheStorage; import jadx.core.utils.ErrorsCounter; @@ -424,6 +425,10 @@ public class RootNode { return typeUpdate; } + public TypeCompare getTypeCompare() { + return typeUpdate.getTypeCompare(); + } + public ICodeCache getCodeCache() { return codeCache; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java index 93388a8ed..b062702df 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java @@ -33,6 +33,7 @@ import jadx.core.dex.instructions.NewArrayNode; import jadx.core.dex.instructions.SwitchInsn; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.InsnWrapArg; import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.NamedArg; import jadx.core.dex.instructions.args.RegisterArg; @@ -49,6 +50,7 @@ import jadx.core.dex.trycatch.ExcHandlerAttr; import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.dex.visitors.regions.variables.ProcessVariables; import jadx.core.dex.visitors.shrink.CodeShrinkVisitor; +import jadx.core.dex.visitors.typeinference.TypeCompareEnum; import jadx.core.utils.InsnRemover; import jadx.core.utils.InsnUtils; import jadx.core.utils.exceptions.JadxException; @@ -144,6 +146,11 @@ public class ModVisitor extends AbstractVisitor { fixPrimitiveCast(mth, block, i, insn); break; + case IPUT: + case IGET: + fixTypeForFieldAccess(mth, (IndexInsnNode) insn); + break; + default: break; } @@ -152,6 +159,31 @@ public class ModVisitor extends AbstractVisitor { } } + private static void fixTypeForFieldAccess(MethodNode mth, IndexInsnNode insn) { + InsnArg instanceArg = insn.getArg(insn.getType() == InsnType.IGET ? 0 : 1); + if (instanceArg.contains(AFlag.SUPER)) { + return; + } + if (instanceArg.isInsnWrap() && ((InsnWrapArg) instanceArg).getWrapInsn().getType() == InsnType.CAST) { + return; + } + FieldInfo fieldInfo = (FieldInfo) insn.getIndex(); + ArgType clsType = fieldInfo.getDeclClass().getType(); + ArgType instanceType = instanceArg.getType(); + TypeCompareEnum result = mth.root().getTypeCompare().compareTypes(instanceType, clsType); + if (result.isEqual() || (result == TypeCompareEnum.NARROW_BY_GENERIC && !instanceType.isGenericType())) { + return; + } + IndexInsnNode castInsn = new IndexInsnNode(InsnType.CAST, clsType, 1); + castInsn.addArg(instanceArg.duplicate()); + castInsn.add(AFlag.EXPLICIT_CAST); + + InsnArg castArg = InsnArg.wrapInsnIntoArg(castInsn); + castArg.setType(clsType); + insn.replaceArg(instanceArg, castArg); + InsnRemover.unbindArgUsage(mth, instanceArg); + } + private static void replaceConstKeys(ClassNode parentClass, SwitchInsn insn) { int[] keys = insn.getKeys(); int len = keys.length; diff --git a/jadx-core/src/test/java/jadx/tests/integration/types/TestFieldAccess.java b/jadx-core/src/test/java/jadx/tests/integration/types/TestFieldAccess.java new file mode 100644 index 000000000..d304af20b --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/types/TestFieldAccess.java @@ -0,0 +1,31 @@ +package jadx.tests.integration.types; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestFieldAccess extends IntegrationTest { + + public static class TestCls { + private String field; + + static T testPut(T t) { + ((TestCls) t).field = ""; + return t; + } + + static T testGet(T t) { + System.out.println(((TestCls) t).field); + return t; + } + } + + @Test + public void test() { + assertThat(getClassNode(TestCls.class)) + .code() + .doesNotContain("t.field"); + } +}