From c0194d025d2a946abc2630bbe0abc54535395d78 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 3 Aug 2019 14:26:25 +0300 Subject: [PATCH] refactor: fix misuse of immutable type flag --- .../dex/instructions/args/RegisterArg.java | 40 +++++++++++-------- .../core/dex/instructions/args/SSAVar.java | 29 ++++++++++++-- .../java/jadx/core/dex/nodes/MethodNode.java | 6 +-- .../core/dex/visitors/DeboxingVisitor.java | 3 +- .../core/dex/visitors/InitCodeVariables.java | 5 ++- .../jadx/core/dex/visitors/ModVisitor.java | 2 +- .../visitors/regions/LoopRegionVisitor.java | 13 ++++-- .../dex/visitors/regions/RegionMaker.java | 3 +- .../regions/variables/ProcessVariables.java | 7 ++-- .../visitors/shrink/CodeShrinkVisitor.java | 2 +- .../core/dex/visitors/ssa/RenameState.java | 8 +--- .../typeinference/TypeInferenceVisitor.java | 32 ++++++--------- .../visitors/typeinference/TypeSearch.java | 21 ++++------ .../typeinference/TypeSearchVarInfo.java | 7 ++++ .../visitors/typeinference/TypeUpdate.java | 22 +++++++--- .../integration/inline/TestTernaryCast.java | 38 ++++++++++++++++++ 16 files changed, 155 insertions(+), 83 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/inline/TestTernaryCast.java diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java index c536e0b32..9d6827184 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java @@ -4,15 +4,12 @@ import java.util.Objects; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.nodes.InsnNode; import jadx.core.utils.exceptions.JadxRuntimeException; public class RegisterArg extends InsnArg implements Named { - private static final Logger LOG = LoggerFactory.getLogger(RegisterArg.class); public static final String THIS_ARG_NAME = "this"; protected final int regNum; @@ -38,15 +35,6 @@ public class RegisterArg extends InsnArg implements Named { if (sVar == null) { throw new JadxRuntimeException("Can't change type for register without SSA variable: " + this); } - if (contains(AFlag.IMMUTABLE_TYPE)) { - if (!type.isTypeKnown()) { - throw new JadxRuntimeException("Unknown immutable type '" + type + "' in " + this); - } - if (!type.equals(newType)) { - LOG.warn("JADX WARNING: Can't change immutable type from '{}' to '{}' for {}", type, newType, this); - return; - } - } sVar.setType(newType); } @@ -62,9 +50,23 @@ public class RegisterArg extends InsnArg implements Named { return type; } + @Nullable + public ArgType getImmutableType() { + if (contains(AFlag.IMMUTABLE_TYPE)) { + return type; + } + if (sVar != null) { + return sVar.getImmutableType(); + } + return null; + } + @Override public boolean isTypeImmutable() { - return contains(AFlag.IMMUTABLE_TYPE) || (sVar != null && sVar.contains(AFlag.IMMUTABLE_TYPE)); + if (contains(AFlag.IMMUTABLE_TYPE)) { + return true; + } + return sVar != null && sVar.isTypeImmutable(); } public SSAVar getSVar() { @@ -73,9 +75,14 @@ public class RegisterArg extends InsnArg implements Named { void setSVar(@NotNull SSAVar sVar) { this.sVar = sVar; - if (contains(AFlag.IMMUTABLE_TYPE)) { - sVar.add(AFlag.IMMUTABLE_TYPE); + } + + @Override + public void add(AFlag flag) { + if (flag == AFlag.IMMUTABLE_TYPE && !type.isTypeKnown()) { + throw new JadxRuntimeException("Can't mark unknown type as immutable, type: " + type + ", reg: " + this); } + super.add(flag); } @Override @@ -169,8 +176,7 @@ public class RegisterArg extends InsnArg implements Named { @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("(r"); - sb.append(regNum); + sb.append("(r").append(regNum); if (sVar != null) { sb.append('v').append(sVar.getVersion()); } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java index 2f2fed6a0..6d8a10d7d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/SSAVar.java @@ -9,8 +9,8 @@ import java.util.Set; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; -import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.nodes.RegDebugInfoAttr; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.PhiInsn; @@ -20,7 +20,7 @@ import jadx.core.dex.visitors.typeinference.TypeInfo; import jadx.core.utils.StringUtils; import jadx.core.utils.exceptions.JadxRuntimeException; -public class SSAVar extends AttrNode { +public class SSAVar { private final int regNum; private final int version; @@ -66,8 +66,29 @@ public class SSAVar extends AttrNode { return useList.size(); } - // must be used only from RegisterArg#setType() - void setType(ArgType type) { + @Nullable + public ArgType getImmutableType() { + if (assign.contains(AFlag.IMMUTABLE_TYPE)) { + return assign.getInitType(); + } + for (RegisterArg useArg : useList) { + if (useArg.contains(AFlag.IMMUTABLE_TYPE)) { + return useArg.getInitType(); + } + } + return null; + } + + public boolean isTypeImmutable() { + return getImmutableType() != null; + } + + public void setType(ArgType type) { + ArgType imType = getImmutableType(); + if (imType != null && !imType.equals(type)) { + throw new JadxRuntimeException("Can't change immutable type " + imType + " to " + type + " for " + this); + } + typeInfo.setType(type); if (codeVar != null) { codeVar.setType(type); diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index 9c5a78905..a71449493 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -244,12 +244,12 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { return; } argsList = new ArrayList<>(args.size()); - for (ArgType arg : args) { - RegisterArg regArg = InsnArg.reg(pos, arg); + for (ArgType argType : args) { + RegisterArg regArg = InsnArg.reg(pos, argType); regArg.add(AFlag.METHOD_ARGUMENT); regArg.add(AFlag.IMMUTABLE_TYPE); argsList.add(regArg); - pos += arg.getRegCount(); + pos += argType.getRegCount(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DeboxingVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DeboxingVisitor.java index e18102eca..d37c41a6b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DeboxingVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DeboxingVisitor.java @@ -134,8 +134,7 @@ public class DeboxingVisitor extends AbstractVisitor { private boolean canChangeTypeToPrimitive(RegisterArg arg) { for (SSAVar ssaVar : arg.getSVar().getCodeVar().getSsaVars()) { - RegisterArg assignArg = ssaVar.getAssign(); - if (assignArg.contains(AFlag.IMMUTABLE_TYPE)) { + if (ssaVar.isTypeImmutable()) { return false; } for (RegisterArg useArg : ssaVar.getUseList()) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/InitCodeVariables.java b/jadx-core/src/main/java/jadx/core/dex/visitors/InitCodeVariables.java index c04b7b2b4..944939995 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/InitCodeVariables.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/InitCodeVariables.java @@ -2,6 +2,7 @@ package jadx.core.dex.visitors; import java.util.LinkedHashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -89,8 +90,8 @@ public class InitCodeVariables extends AbstractVisitor { private static void setCodeVarType(CodeVar codeVar, Set vars) { if (vars.size() > 1) { List imTypes = vars.stream() - .filter(var -> var.contains(AFlag.IMMUTABLE_TYPE)) - .map(var -> var.getTypeInfo().getType()) + .map(SSAVar::getImmutableType) + .filter(Objects::nonNull) .filter(ArgType::isTypeKnown) .distinct() .collect(Collectors.toList()); 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 2a7a38132..7e4f51b62 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 @@ -294,7 +294,7 @@ public class ModVisitor extends AbstractVisitor { SSAVar sVar = reg.getSVar(); if (sVar != null) { sVar.getCodeVar().setFinal(true); - sVar.add(AFlag.DONT_INLINE); + sVar.getAssign().add(AFlag.DONT_INLINE); } reg.add(AFlag.SKIP_ARG); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java index e41c4f8c0..5c3ac51af 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java @@ -337,9 +337,16 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor if (!iterableArg.isRegister() || !iterableType.isObject()) { return true; } - // TODO: add checks - iterableType = ArgType.generic(iterableType.getObject(), varType); - iterableArg.setType(iterableType); + ArgType genericType = ArgType.generic(iterableType.getObject(), varType); + if (iterableArg.isRegister()) { + ArgType immutableType = ((RegisterArg) iterableArg).getImmutableType(); + if (immutableType != null && !immutableType.equals(genericType)) { + // can't change type + // allow to iterate over not generified collection only for Object vars + return varType.equals(ArgType.OBJECT); + } + } + iterableArg.setType(genericType); return true; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java index 5ca05cb63..d4b831947 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java @@ -793,8 +793,7 @@ public class RegionMaker { LOG.debug("Fixing incorrect switch cases order, method: {}", mth); blocksMap = reOrderSwitchCases(blocksMap, fallThroughCases); if (isBadCasesOrder(blocksMap, fallThroughCases)) { - LOG.error("Can't fix incorrect switch cases order, method: {}", mth); - mth.add(AFlag.INCONSISTENT_CODE); + mth.addWarn("Can't fix incorrect switch cases order"); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/ProcessVariables.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/ProcessVariables.java index 702eda545..a709995e7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/ProcessVariables.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/variables/ProcessVariables.java @@ -72,11 +72,10 @@ public class ProcessVariables extends AbstractVisitor { codeVar.setType(ArgType.UNKNOWN); unknownTypesCount++; } else { - codeVar.getSsaVars().stream() - .filter(ssaVar -> ssaVar.contains(AFlag.IMMUTABLE_TYPE)) + codeVar.getSsaVars() .forEach(ssaVar -> { - ArgType ssaType = ssaVar.getAssign().getInitType(); - if (ssaType.isTypeKnown()) { + ArgType ssaType = ssaVar.getImmutableType(); + if (ssaType != null && ssaType.isTypeKnown()) { TypeCompare comparator = mth.root().getTypeUpdate().getTypeCompare(); TypeCompareEnum result = comparator.compareTypes(ssaType, codeVarType); if (result == TypeCompareEnum.CONFLICT || result.isNarrow()) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java index 3ab65e143..f2fad13b1 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/shrink/CodeShrinkVisitor.java @@ -75,7 +75,7 @@ public class CodeShrinkVisitor extends AbstractVisitor { private static void checkInline(MethodNode mth, BlockNode block, InsnList insnList, List wrapList, ArgsInfo argsInfo, RegisterArg arg) { SSAVar sVar = arg.getSVar(); - if (sVar == null || sVar.contains(AFlag.DONT_INLINE)) { + if (sVar == null || sVar.getAssign().contains(AFlag.DONT_INLINE)) { return; } // allow inline only one use arg diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/RenameState.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/RenameState.java index 95e5f5512..61a551a01 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/RenameState.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/RenameState.java @@ -2,7 +2,6 @@ package jadx.core.dex.visitors.ssa; import java.util.Arrays; -import jadx.core.dex.attributes.AFlag; import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.BlockNode; @@ -23,13 +22,10 @@ final class RenameState { new int[regsCount]); RegisterArg thisArg = mth.getThisArg(); if (thisArg != null) { - SSAVar ssaVar = state.startVar(thisArg); - ssaVar.add(AFlag.THIS); - ssaVar.add(AFlag.METHOD_ARGUMENT); + state.startVar(thisArg); } for (RegisterArg arg : mth.getArgRegs()) { - SSAVar ssaVar = state.startVar(arg); - ssaVar.add(AFlag.METHOD_ARGUMENT); + state.startVar(arg); } return state; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java index b72b307a0..f0b3aeef3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInferenceVisitor.java @@ -107,7 +107,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor { for (SSAVar var : mth.getSVars()) { ArgType type = var.getTypeInfo().getType(); if (!type.isTypeKnown() - && !var.getAssign().isTypeImmutable() + && !var.isTypeImmutable() && !tryDeduceType(mth, var, type)) { resolved = false; } @@ -131,20 +131,9 @@ public final class TypeInferenceVisitor extends AbstractVisitor { private boolean setImmutableType(SSAVar ssaVar) { try { - ArgType codeVarType = ssaVar.getCodeVar().getType(); - if (codeVarType != null) { - return applyImmutableType(ssaVar, codeVarType); - } - RegisterArg assignArg = ssaVar.getAssign(); - if (assignArg.isTypeImmutable()) { - return applyImmutableType(ssaVar, assignArg.getInitType()); - } - if (ssaVar.contains(AFlag.IMMUTABLE_TYPE)) { - for (RegisterArg arg : ssaVar.getUseList()) { - if (arg.isTypeImmutable()) { - return applyImmutableType(ssaVar, arg.getInitType()); - } - } + ArgType immutableType = ssaVar.getImmutableType(); + if (immutableType != null) { + return applyImmutableType(ssaVar, immutableType); } return false; } catch (Exception e) { @@ -166,7 +155,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor { TypeUpdateResult result = typeUpdate.apply(ssaVar, initType); if (result == TypeUpdateResult.REJECT) { if (Consts.DEBUG) { - LOG.warn("Initial immutable type set rejected: {} -> {}", ssaVar, initType); + LOG.info("Reject initial immutable type {} for {}", initType, ssaVar); } return false; } @@ -236,8 +225,13 @@ public final class TypeInferenceVisitor extends AbstractVisitor { } private void addAssignBound(TypeInfo typeInfo, RegisterArg assign) { + ArgType immutableType = assign.getImmutableType(); + if (immutableType != null) { + addBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, immutableType)); + return; + } InsnNode insn = assign.getParentInsn(); - if (insn == null || assign.isTypeImmutable()) { + if (insn == null || insn.getResult() == null) { addBound(typeInfo, new TypeBoundConst(BoundEnum.ASSIGN, assign.getInitType())); return; } @@ -436,12 +430,12 @@ public final class TypeInferenceVisitor extends AbstractVisitor { } private void processIncompatiblePrimitives(MethodNode mth, SSAVar var) { - if (var.getAssign().getType() == ArgType.BOOLEAN) { + if (var.getTypeInfo().getType() == ArgType.BOOLEAN) { for (ITypeBound bound : var.getTypeInfo().getBounds()) { if (bound.getBound() == BoundEnum.USE && bound.getType().isPrimitive() && bound.getType() != ArgType.BOOLEAN) { InsnNode insn = bound.getArg().getParentInsn(); - if (insn.getType() == InsnType.CAST) { + if (insn == null || insn.getType() == InsnType.CAST) { continue; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearch.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearch.java index a6d600531..ab5517339 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearch.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearch.java @@ -77,10 +77,7 @@ public class TypeSearch { for (TypeSearchVarInfo var : resolvedVars) { SSAVar ssaVar = var.getVar(); ArgType resolvedType = var.getCurrentType(); - ssaVar.getAssign().setType(resolvedType); - for (RegisterArg arg : ssaVar.getUseList()) { - arg.setType(resolvedType); - } + ssaVar.setType(resolvedType); } boolean applySuccess = true; for (TypeSearchVarInfo var : resolvedVars) { @@ -194,18 +191,14 @@ public class TypeSearch { private void fillTypeCandidates(SSAVar ssaVar) { TypeSearchVarInfo varInfo = state.getVarInfo(ssaVar); - ArgType currentType = ssaVar.getTypeInfo().getType(); - if (currentType.isTypeKnown()) { - varInfo.setTypeResolved(true); - varInfo.setCurrentType(currentType); - varInfo.setCandidateTypes(Collections.emptyList()); + ArgType immutableType = ssaVar.getImmutableType(); + if (immutableType != null) { + varInfo.markResolved(immutableType); return; } - if (ssaVar.getAssign().isTypeImmutable()) { - ArgType initType = ssaVar.getAssign().getInitType(); - varInfo.setTypeResolved(true); - varInfo.setCurrentType(initType); - varInfo.setCandidateTypes(Collections.emptyList()); + ArgType currentType = ssaVar.getTypeInfo().getType(); + if (currentType.isTypeKnown()) { + varInfo.markResolved(currentType); return; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearchVarInfo.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearchVarInfo.java index 3a0cc2407..18a878c3d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearchVarInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeSearchVarInfo.java @@ -1,5 +1,6 @@ package jadx.core.dex.visitors.typeinference; +import java.util.Collections; import java.util.List; import jadx.core.dex.instructions.args.ArgType; @@ -17,6 +18,12 @@ public class TypeSearchVarInfo { this.var = var; } + public void markResolved(ArgType type) { + this.currentType = type; + this.typeResolved = true; + this.candidateTypes = Collections.emptyList(); + } + public void reset() { if (typeResolved) { return; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java index 9d8de7ae0..690f53663 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdate.java @@ -122,6 +122,13 @@ public final class TypeUpdate { private TypeUpdateResult updateTypeForSsaVar(TypeUpdateInfo updateInfo, SSAVar ssaVar, ArgType candidateType) { TypeInfo typeInfo = ssaVar.getTypeInfo(); + ArgType immutableType = ssaVar.getImmutableType(); + if (immutableType != null && !Objects.equals(immutableType, candidateType)) { + if (Consts.DEBUG) { + LOG.info("Reject change immutable type {} to {} for {}", immutableType, candidateType, ssaVar); + } + return REJECT; + } if (!inBounds(updateInfo, typeInfo.getBounds(), candidateType)) { if (Consts.DEBUG) { LOG.debug("Reject type '{}' for {} by bounds: {}", candidateType, ssaVar, typeInfo.getBounds()); @@ -363,15 +370,20 @@ public final class TypeUpdate { boolean assignChanged = isAssign(insn, arg); InsnArg changeArg = assignChanged ? insn.getArg(0) : insn.getResult(); - // allow result to be wider - TypeCompareEnum cmp = comparator.compareTypes(candidateType, changeArg.getType()); - boolean correctType = cmp.isEqual() || (assignChanged ? cmp.isWider() : cmp.isNarrow()); + boolean correctType; + if (changeArg.getType().isTypeKnown()) { + // allow result to be wider + TypeCompareEnum cmp = comparator.compareTypes(candidateType, changeArg.getType()); + correctType = cmp.isEqual() || (assignChanged ? cmp.isWider() : cmp.isNarrow()); + } else { + correctType = true; + } TypeUpdateResult result = updateTypeChecked(updateInfo, changeArg, candidateType); if (result == SAME && !correctType) { if (Consts.DEBUG) { - LOG.debug("Move insn types mismatch: {} -> {}, insn: {}", - candidateType, changeArg.getType(), insn); + LOG.debug("Move insn types mismatch: {} -> {}, change arg: {}, insn: {}", + candidateType, changeArg.getType(), changeArg, insn); } return REJECT; } diff --git a/jadx-core/src/test/java/jadx/tests/integration/inline/TestTernaryCast.java b/jadx-core/src/test/java/jadx/tests/integration/inline/TestTernaryCast.java new file mode 100644 index 000000000..6b078b020 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/inline/TestTernaryCast.java @@ -0,0 +1,38 @@ +package jadx.tests.integration.inline; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.hamcrest.MatcherAssert.assertThat; + +public class TestTernaryCast extends IntegrationTest { + + public static class TestCls { + public String test(boolean b, Object obj, CharSequence cs) { + return (String) (b ? obj : cs); + } + + public void check() { + assertThat(test(true, "a", "b"), Matchers.is("a")); + assertThat(test(false, "a", "b"), Matchers.is("b")); + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("return (String) (b ? obj : cs);")); + } + + @Test + public void testNoDebug() { + noDebugInfo(); + getClassNode(TestCls.class); + } +}