fix: improve boolean type handling in type inference

This commit is contained in:
Skylot
2020-07-05 16:45:29 +01:00
parent e81ba1c431
commit 31434186ab
22 changed files with 246 additions and 209 deletions
@@ -29,22 +29,6 @@ public enum PrimitiveType {
return longName;
}
public static PrimitiveType getWidest(PrimitiveType a, PrimitiveType b) {
if (a.ordinal() > b.ordinal()) {
return a;
} else {
return b;
}
}
public static PrimitiveType getSmaller(PrimitiveType a, PrimitiveType b) {
if (a.ordinal() < b.ordinal()) {
return a;
} else {
return b;
}
}
@Override
public String toString() {
return longName;
@@ -7,6 +7,7 @@ import org.jetbrains.annotations.Nullable;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class RegisterArg extends InsnArg implements Named {
@@ -127,15 +128,17 @@ public class RegisterArg extends InsnArg implements Named {
@Override
public RegisterArg duplicate() {
return duplicate(getRegNum(), sVar);
return duplicate(getRegNum(), getInitType(), sVar);
}
public RegisterArg duplicate(ArgType initType) {
return duplicate(getRegNum(), initType, sVar);
}
public RegisterArg duplicate(@Nullable SSAVar ssaVar) {
return duplicate(getRegNum(), ssaVar);
public RegisterArg duplicateWithNewSSAVar(MethodNode mth) {
RegisterArg duplicate = duplicate(regNum, getInitType(), null);
mth.makeNewSVar(duplicate);
return duplicate;
}
public RegisterArg duplicate(int regNum, @Nullable SSAVar sVar) {
@@ -201,23 +201,27 @@ public class ModVisitor extends AbstractVisitor {
if (castArg.getType() == ArgType.BOOLEAN) {
ArgType type = insn.getResult().getType();
if (type.isPrimitive()) {
InsnArg zero = new LiteralArg(0, type);
long litVal = 1;
if (type == ArgType.DOUBLE) {
litVal = DOUBLE_TO_BITS;
} else if (type == ArgType.FLOAT) {
litVal = FLOAT_TO_BITS;
}
InsnArg one = new LiteralArg(litVal, type);
IfNode ifNode = new IfNode(IfOp.EQ, -1, castArg, LiteralArg.TRUE);
IfCondition condition = IfCondition.fromIfNode(ifNode);
TernaryInsn ternary = new TernaryInsn(condition, insn.getResult(), one, zero);
TernaryInsn ternary = makeBooleanConvertInsn(insn.getResult(), castArg, type);
replaceInsn(mth, block, i, ternary);
}
}
}
public static TernaryInsn makeBooleanConvertInsn(RegisterArg result, InsnArg castArg, ArgType type) {
InsnArg zero = new LiteralArg(0, type);
long litVal = 1;
if (type == ArgType.DOUBLE) {
litVal = DOUBLE_TO_BITS;
} else if (type == ArgType.FLOAT) {
litVal = FLOAT_TO_BITS;
}
InsnArg one = new LiteralArg(litVal, type);
IfNode ifNode = new IfNode(IfOp.EQ, -1, castArg, LiteralArg.TRUE);
IfCondition condition = IfCondition.fromIfNode(ifNode);
return new TernaryInsn(condition, result, one, zero);
}
private void replaceConstInAnnotations(ClassNode cls) {
if (cls.root().getArgs().isReplaceConsts()) {
replaceConstsInAnnotationForAttrNode(cls, cls);
@@ -84,8 +84,13 @@ public class TypeCompare {
}
}
if (firstPrimitive && secondPrimitive) {
int comparePrimitives = first.getPrimitiveType().compareTo(second.getPrimitiveType());
return comparePrimitives > 0 ? WIDER : NARROW;
PrimitiveType firstPrimitiveType = first.getPrimitiveType();
PrimitiveType secondPrimitiveType = second.getPrimitiveType();
if (firstPrimitiveType == PrimitiveType.BOOLEAN
|| secondPrimitiveType == PrimitiveType.BOOLEAN) {
return CONFLICT;
}
return firstPrimitiveType.compareTo(secondPrimitiveType) > 0 ? WIDER : NARROW;
}
LOG.warn("Type compare function not complete, can't compare {} and {}", first, second);
@@ -1,11 +1,13 @@
package jadx.core.dex.visitors.typeinference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@@ -23,12 +25,12 @@ import jadx.core.dex.instructions.InvokeNode;
import jadx.core.dex.instructions.InvokeType;
import jadx.core.dex.instructions.PhiInsn;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.CodeVar;
import jadx.core.dex.instructions.args.InsnArg;
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.TernaryInsn;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IMethodDetails;
import jadx.core.dex.nodes.InsnNode;
@@ -40,9 +42,11 @@ import jadx.core.dex.visitors.AttachMethodDetails;
import jadx.core.dex.visitors.ConstInlineVisitor;
import jadx.core.dex.visitors.InitCodeVariables;
import jadx.core.dex.visitors.JadxVisitor;
import jadx.core.dex.visitors.ModVisitor;
import jadx.core.dex.visitors.blocksmaker.BlockSplitter;
import jadx.core.dex.visitors.ssa.SSATransform;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.InsnList;
import jadx.core.utils.InsnUtils;
import jadx.core.utils.Utils;
@@ -60,11 +64,20 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
private RootNode root;
private TypeUpdate typeUpdate;
private List<Function<MethodNode, Boolean>> resolvers;
@Override
public void init(RootNode root) {
this.root = root;
this.typeUpdate = root.getTypeUpdate();
this.resolvers = Arrays.asList(
this::runTypePropagation,
this::tryDeduceTypes,
this::trySplitConstInsns,
this::tryToFixIncompatiblePrimitives,
this::tryInsertAdditionalMove,
this::runMultiVariableSearch,
this::tryRemoveGenerics);
}
@Override
@@ -75,30 +88,24 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
if (Consts.DEBUG_TYPE_INFERENCE) {
LOG.info("Start type inference in method: {}", mth);
}
if (resolveTypes(mth)) {
for (SSAVar var : new ArrayList<>(mth.getSVars())) {
processIncompatiblePrimitives(mth, var);
for (Function<MethodNode, Boolean> resolver : resolvers) {
if (resolver.apply(mth) && checkTypes(mth)) {
return;
}
}
}
private boolean resolveTypes(MethodNode mth) {
if (runTypePropagation(mth)) {
return true;
/**
* Check if all types resolved
*/
private boolean checkTypes(MethodNode mth) {
for (SSAVar var : mth.getSVars()) {
ArgType type = var.getTypeInfo().getType();
if (!type.isTypeKnown()) {
return false;
}
}
if (trySplitConstInsns(mth)) {
return true;
}
if (tryInsertAdditionalMove(mth)) {
return true;
}
if (runMultiVariableSearch(mth)) {
return true;
}
if (tryRemoveGenerics(mth)) {
return true;
}
return false;
return true;
}
/**
@@ -114,23 +121,12 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
// start initial type propagation
ssaVars.forEach(var -> setImmutableType(mth, var));
ssaVars.forEach(var -> setBestType(mth, var));
// try other types if type is still unknown
boolean resolved = true;
for (SSAVar var : ssaVars) {
ArgType type = var.getTypeInfo().getType();
if (!type.isTypeKnown()
&& !var.isTypeImmutable()
&& !tryDeduceType(mth, var, type)) {
resolved = false;
}
}
return resolved;
return true;
}
private boolean runMultiVariableSearch(MethodNode mth) {
TypeSearch typeSearch = new TypeSearch(mth);
try {
TypeSearch typeSearch = new TypeSearch(mth);
if (!typeSearch.run()) {
mth.addWarnComment("Multi-variable type inference failed");
}
@@ -359,13 +355,30 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
return list;
}
private boolean tryDeduceType(MethodNode mth, SSAVar var, @Nullable ArgType type) {
private boolean tryDeduceTypes(MethodNode mth) {
boolean fixed = false;
for (SSAVar ssaVar : mth.getSVars()) {
if (deduceType(mth, ssaVar)) {
fixed = true;
}
}
return fixed;
}
private boolean deduceType(MethodNode mth, SSAVar var) {
if (var.isTypeImmutable()) {
return false;
}
ArgType type = var.getTypeInfo().getType();
if (type.isTypeKnown()) {
return false;
}
// try best type from bounds again
if (setBestType(mth, var)) {
return true;
}
// try all possible types (useful for primitives)
if (type != null && tryPossibleTypes(mth, var, type)) {
if (tryPossibleTypes(mth, var, type)) {
return true;
}
// for objects try super types
@@ -379,7 +392,8 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
boolean resolved = true;
for (SSAVar var : mth.getSVars()) {
ArgType type = var.getTypeInfo().getType();
if (!type.isTypeKnown() && !var.isTypeImmutable()
if (!type.isTypeKnown()
&& !var.isTypeImmutable()
&& !tryRawType(mth, var)) {
resolved = false;
}
@@ -462,44 +476,47 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
}
private boolean tryInsertAdditionalMove(MethodNode mth) {
boolean moveAdded = false;
int insnsAdded = 0;
for (SSAVar var : new ArrayList<>(mth.getSVars())) {
if (tryInsertAdditionalInsn(mth, var)) {
moveAdded = true;
}
insnsAdded += tryInsertAdditionalInsn(mth, var);
}
if (!moveAdded) {
if (insnsAdded == 0) {
return false;
}
mth.addDebugComment("Additional " + insnsAdded + " move instruction added to help type inference");
InitCodeVariables.rerun(mth);
return runTypePropagation(mth);
if (runTypePropagation(mth) && checkTypes(mth)) {
return true;
}
return tryDeduceTypes(mth);
}
/**
* Add MOVE instruction before PHI in bound blocks to make 'soft' type link.
* This allows to use different types in blocks merged by PHI.
*/
private boolean tryInsertAdditionalInsn(MethodNode mth, SSAVar var) {
private int tryInsertAdditionalInsn(MethodNode mth, SSAVar var) {
if (var.getTypeInfo().getType().isTypeKnown()) {
return false;
return 0;
}
List<PhiInsn> usedInPhiList = var.getUsedInPhi();
if (usedInPhiList.isEmpty()) {
return false;
return 0;
}
InsnNode assignInsn = var.getAssign().getAssignInsn();
if (assignInsn != null) {
InsnType assignType = assignInsn.getType();
if (assignType == InsnType.CONST) {
return false;
return 0;
}
if (assignType == InsnType.MOVE && var.getUseCount() == 1) {
return false;
return 0;
}
}
for (PhiInsn phiInsn : usedInPhiList) {
if (!insertMoveForPhi(mth, phiInsn, var, false)) {
return false;
return 0;
}
}
@@ -507,8 +524,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
for (PhiInsn phiInsn : usedInPhiList) {
insertMoveForPhi(mth, phiInsn, var, true);
}
mth.addComment("JADX INFO: additional move instructions added (" + usedInPhiList.size() + ") to help type inference");
return true;
return usedInPhiList.size();
}
private boolean insertMoveForPhi(MethodNode mth, PhiInsn phiInsn, SSAVar var, boolean apply) {
@@ -583,42 +599,87 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
return false;
}
private void processIncompatiblePrimitives(MethodNode mth, SSAVar var) {
if (var.getTypeInfo().getType() == ArgType.BOOLEAN) {
for (ITypeBound bound : var.getTypeInfo().getBounds()) {
if (bound.getBound() == BoundEnum.USE
&& bound.getType().isPrimitive()
&& bound.getType() != ArgType.BOOLEAN
&& bound.getArg() != null) {
InsnNode insn = bound.getArg().getParentInsn();
if (insn == null || insn.getType() == InsnType.CAST) {
continue;
}
IndexInsnNode castNode = new IndexInsnNode(InsnType.CAST, bound.getType(), 1);
castNode.addArg(bound.getArg());
castNode.setResult(InsnArg.reg(bound.getArg().getRegNum(), bound.getType()));
SSAVar newVar = mth.makeNewSVar(castNode.getResult());
CodeVar codeVar = new CodeVar();
codeVar.setType(bound.getType());
newVar.setCodeVar(codeVar);
newVar.getTypeInfo().setType(bound.getType());
for (int i = insn.getArgsCount() - 1; i >= 0; i--) {
if (insn.getArg(i) == bound.getArg()) {
insn.setArg(i, castNode.getResult().duplicate());
break;
}
}
BlockNode blockNode = BlockUtils.getBlockByInsn(mth, insn);
if (blockNode != null) {
List<InsnNode> insnList = blockNode.getInstructions();
insnList.add(insnList.indexOf(insn), castNode);
}
}
private boolean tryToFixIncompatiblePrimitives(MethodNode mth) {
boolean fixed = false;
for (SSAVar var : new ArrayList<>(mth.getSVars())) {
if (processIncompatiblePrimitives(mth, var)) {
fixed = true;
}
}
if (!fixed) {
return false;
}
InitCodeVariables.rerun(mth);
return runTypePropagation(mth);
}
private boolean processIncompatiblePrimitives(MethodNode mth, SSAVar var) {
TypeInfo typeInfo = var.getTypeInfo();
if (typeInfo.getType().isTypeKnown()) {
return false;
}
boolean boolAssign = false;
for (ITypeBound bound : typeInfo.getBounds()) {
if (bound.getBound() == BoundEnum.ASSIGN && bound.getType().equals(ArgType.BOOLEAN)) {
boolAssign = true;
break;
}
}
if (!boolAssign) {
return false;
}
boolean fixed = false;
for (ITypeBound bound : typeInfo.getBounds()) {
if (bound.getBound() == BoundEnum.USE
&& fixBooleanUsage(mth, bound)) {
fixed = true;
}
}
return fixed;
}
private boolean fixBooleanUsage(MethodNode mth, ITypeBound bound) {
ArgType boundType = bound.getType();
if (!boundType.isPrimitive() || boundType == ArgType.BOOLEAN) {
return false;
}
RegisterArg boundArg = bound.getArg();
if (boundArg == null) {
return false;
}
InsnNode insn = boundArg.getParentInsn();
if (insn == null) {
return false;
}
BlockNode blockNode = BlockUtils.getBlockByInsn(mth, insn);
if (blockNode == null) {
return false;
}
List<InsnNode> insnList = blockNode.getInstructions();
int insnIndex = InsnList.getIndex(insnList, insn);
if (insnIndex == -1) {
return false;
}
if (insn.getType() == InsnType.CAST) {
// replace cast
ArgType type = (ArgType) ((IndexInsnNode) insn).getIndex();
TernaryInsn convertInsn = prepareBooleanConvertInsn(insn.getResult(), boundArg, type);
BlockUtils.replaceInsn(mth, blockNode, insnIndex, convertInsn);
} else {
// insert before insn
RegisterArg resultArg = boundArg.duplicateWithNewSSAVar(mth);
TernaryInsn convertInsn = prepareBooleanConvertInsn(resultArg, boundArg, boundType);
insnList.add(insnIndex, convertInsn);
insn.replaceArg(bound.getArg(), convertInsn.getResult().duplicate());
}
return true;
}
private TernaryInsn prepareBooleanConvertInsn(RegisterArg resultArg, RegisterArg boundArg, ArgType useType) {
RegisterArg useArg = boundArg.getSVar().getAssign().duplicate();
TernaryInsn convertInsn = ModVisitor.makeBooleanConvertInsn(resultArg, useArg, useType);
convertInsn.add(AFlag.SYNTHETIC);
return convertInsn;
}
}
@@ -23,6 +23,7 @@ import static jadx.core.dex.instructions.args.ArgType.INT;
import static jadx.core.dex.instructions.args.ArgType.NARROW;
import static jadx.core.dex.instructions.args.ArgType.NARROW_INTEGRAL;
import static jadx.core.dex.instructions.args.ArgType.OBJECT;
import static jadx.core.dex.instructions.args.ArgType.SHORT;
import static jadx.core.dex.instructions.args.ArgType.STRING;
import static jadx.core.dex.instructions.args.ArgType.UNKNOWN;
import static jadx.core.dex.instructions.args.ArgType.UNKNOWN_ARRAY;
@@ -52,8 +53,6 @@ public class TypeCompareTest {
public void compareTypes() {
firstIsNarrow(INT, UNKNOWN);
firstIsNarrow(BOOLEAN, INT);
firstIsNarrow(array(UNKNOWN), UNKNOWN);
firstIsNarrow(array(UNKNOWN), NARROW);
}
@@ -62,7 +61,9 @@ public class TypeCompareTest {
public void comparePrimitives() {
check(INT, UNKNOWN_OBJECT, TypeCompareEnum.CONFLICT);
check(INT, OBJECT, TypeCompareEnum.CONFLICT);
check(INT, BOOLEAN, TypeCompareEnum.CONFLICT);
check(INT, CHAR, TypeCompareEnum.WIDER);
check(INT, SHORT, TypeCompareEnum.WIDER);
firstIsNarrow(CHAR, NARROW_INTEGRAL);
firstIsNarrow(array(CHAR), UNKNOWN_OBJECT);
@@ -2,16 +2,14 @@ package jadx.tests.integration.conditions;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestBooleanToByte extends SmaliTest {
// @formatter:off
/**
/*
private boolean showConsent;
public void write(byte b) {
@@ -24,9 +22,8 @@ public class TestBooleanToByte extends SmaliTest {
// @formatter:on
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliWithPath("conditions", "TestBooleanToByte");
String code = cls.getCode().toString();
assertThat(code, containsString("write(this.showConsent ? (byte) 1 : 0);"));
assertThat(getClassNodeFromSmali())
.code()
.containsOne("write(this.showConsent ? (byte) 1 : 0);");
}
}
@@ -2,16 +2,14 @@ package jadx.tests.integration.conditions;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestBooleanToChar extends SmaliTest {
// @formatter:off
/**
/*
private boolean showConsent;
public void write(char b) {
@@ -20,13 +18,12 @@ public class TestBooleanToChar extends SmaliTest {
public void writeToParcel(TestBooleanToChar testBooleanToChar) {
testBooleanToChar.write(this.showConsent ? (char) 1 : 0);
}
*/
*/
// @formatter:on
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliWithPath("conditions", "TestBooleanToChar");
String code = cls.getCode().toString();
assertThat(code, containsString("write(this.showConsent ? (char) 1 : 0);"));
assertThat(getClassNodeFromSmali())
.code()
.containsOne("write(this.showConsent ? (char) 1 : 0);");
}
}
@@ -2,16 +2,14 @@ package jadx.tests.integration.conditions;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestBooleanToDouble extends SmaliTest {
// @formatter:off
/**
/*
private boolean showConsent;
public void write(double d) {
@@ -24,9 +22,8 @@ public class TestBooleanToDouble extends SmaliTest {
// @formatter:on
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliWithPath("conditions", "TestBooleanToDouble");
String code = cls.getCode().toString();
assertThat(code, containsString("write(this.showConsent ? 1.0d : 0.0d);"));
assertThat(getClassNodeFromSmali())
.code()
.containsOne("write(this.showConsent ? 1.0d : 0.0d);");
}
}
@@ -2,16 +2,14 @@ package jadx.tests.integration.conditions;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestBooleanToFloat extends SmaliTest {
// @formatter:off
/**
/*
private boolean showConsent;
public void write(float f) {
@@ -24,9 +22,8 @@ public class TestBooleanToFloat extends SmaliTest {
// @formatter:on
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliWithPath("conditions", "TestBooleanToFloat");
String code = cls.getCode().toString();
assertThat(code, containsString("write(this.showConsent ? 1.0f : 0.0f);"));
assertThat(getClassNodeFromSmali())
.code()
.containsOne("write(this.showConsent ? 1.0f : 0.0f);");
}
}
@@ -2,16 +2,14 @@ package jadx.tests.integration.conditions;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestBooleanToInt extends SmaliTest {
//@formatter:off
/**
// @formatter:off
/*
private boolean showConsent;
public void write(int b) {
@@ -21,12 +19,11 @@ public class TestBooleanToInt extends SmaliTest {
testBooleanToInt.write(this.showConsent ? 1 : 0);
}
*/
//@formatter:on
// @formatter:on
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliWithPath("conditions", "TestBooleanToInt");
String code = cls.getCode().toString();
assertThat(code, containsString("write(this.showConsent ? 1 : 0);"));
assertThat(getClassNodeFromSmali())
.code()
.containsOne("write(this.showConsent ? 1 : 0);");
}
}
@@ -2,16 +2,14 @@ package jadx.tests.integration.conditions;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestBooleanToLong extends SmaliTest {
// @formatter:off
/**
/*
private boolean showConsent;
public void write(long j) {
@@ -24,9 +22,8 @@ public class TestBooleanToLong extends SmaliTest {
// @formatter:on
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliWithPath("conditions", "TestBooleanToLong");
String code = cls.getCode().toString();
assertThat(code, containsString("write(this.showConsent ? 1 : 0);"));
assertThat(getClassNodeFromSmali())
.code()
.containsOne("write(this.showConsent ? 1 : 0);");
}
}
@@ -2,16 +2,14 @@ package jadx.tests.integration.conditions;
import org.junit.jupiter.api.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestBooleanToShort extends SmaliTest {
// @formatter:off
/**
/*
private boolean showConsent;
public void write(short b) {
@@ -24,9 +22,8 @@ public class TestBooleanToShort extends SmaliTest {
// @formatter:on
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliWithPath("conditions", "TestBooleanToShort");
String code = cls.getCode().toString();
assertThat(code, containsString("write(this.showConsent ? (short) 1 : 0);"));
assertThat(getClassNodeFromSmali())
.code()
.containsOne("write(this.showConsent ? (short) 1 : 0);");
}
}
@@ -20,7 +20,7 @@ public class TestIssue13a extends IntegrationTest {
private static final String TAG = "Parcel";
private static final Map<ClassLoader, Map<String, Parcelable.Creator<?>>> M_CREATORS = new HashMap<>();
@SuppressWarnings("unchecked")
@SuppressWarnings({ "unchecked", "ConstantConditions", "Java8MapApi", "rawtypes" })
public final <T extends Parcelable> T test(ClassLoader loader) {
String name = readString();
if (name == null) {
@@ -12,7 +12,7 @@ import static org.hamcrest.Matchers.not;
public class TestTypeResolver10 extends SmaliTest {
/**
/*
* Method argument assigned with different types in separate branches
*/
@@ -1,16 +1,16 @@
.class public LTestBooleanToByte;
.class public Lconditions/TestBooleanToByte;
.super Ljava/lang/Object;
.field private showConsent:Z
.method public writeToParcel(LTestBooleanToByte;)V
.method public writeToParcel(Lconditions/TestBooleanToByte;)V
.locals 0
iget-boolean p1, p0, LTestBooleanToByte;->showConsent:Z
iget-boolean p1, p0, Lconditions/TestBooleanToByte;->showConsent:Z
int-to-byte p1, p1
invoke-virtual {p0, p1}, LTestBooleanToByte;->write(B)V
invoke-virtual {p0, p1}, Lconditions/TestBooleanToByte;->write(B)V
return-void
.end method
@@ -1,16 +1,16 @@
.class public LTestBooleanToChar;
.class public Lconditions/TestBooleanToChar;
.super Ljava/lang/Object;
.field private showConsent:Z
.method public writeToParcel(LTestBooleanToChar;)V
.method public writeToParcel(Lconditions/TestBooleanToChar;)V
.locals 0
iget-boolean p1, p0, LTestBooleanToChar;->showConsent:Z
iget-boolean p1, p0, Lconditions/TestBooleanToChar;->showConsent:Z
int-to-char p1, p1
invoke-virtual {p0, p1}, LTestBooleanToChar;->write(C)V
invoke-virtual {p0, p1}, Lconditions/TestBooleanToChar;->write(C)V
return-void
.end method
@@ -1,16 +1,16 @@
.class public LTestBooleanToDouble;
.class public Lconditions/TestBooleanToDouble;
.super Ljava/lang/Object;
.field private showConsent:Z
.method public writeToParcel(LTestBooleanToDouble;)V
.method public writeToParcel(Lconditions/TestBooleanToDouble;)V
.locals 0
iget-boolean p1, p0, LTestBooleanToDouble;->showConsent:Z
iget-boolean p1, p0, Lconditions/TestBooleanToDouble;->showConsent:Z
int-to-double p1, p1
invoke-virtual {p0, p1}, LTestBooleanToDouble;->write(D)V
invoke-virtual {p0, p1}, Lconditions/TestBooleanToDouble;->write(D)V
return-void
.end method
@@ -1,16 +1,16 @@
.class public LTestBooleanToFloat;
.class public Lconditions/TestBooleanToFloat;
.super Ljava/lang/Object;
.field private showConsent:Z
.method public writeToParcel(LTestBooleanToFloat;)V
.method public writeToParcel(Lconditions/TestBooleanToFloat;)V
.locals 0
iget-boolean p1, p0, LTestBooleanToFloat;->showConsent:Z
iget-boolean p1, p0, Lconditions/TestBooleanToFloat;->showConsent:Z
int-to-float p1, p1
invoke-virtual {p0, p1}, LTestBooleanToFloat;->write(F)V
invoke-virtual {p0, p1}, Lconditions/TestBooleanToFloat;->write(F)V
return-void
.end method
@@ -1,14 +1,14 @@
.class public LTestBooleanToInt;
.class public Lconditions/TestBooleanToInt;
.super Ljava/lang/Object;
.field private showConsent:Z
.method public writeToParcel(LTestBooleanToInt;)V
.method public writeToParcel(Lconditions/TestBooleanToInt;)V
.locals 0
iget-boolean p1, p0, LTestBooleanToInt;->showConsent:Z
iget-boolean p1, p0, Lconditions/TestBooleanToInt;->showConsent:Z
invoke-virtual {p0, p1}, LTestBooleanToInt;->write(I)V
invoke-virtual {p0, p1}, Lconditions/TestBooleanToInt;->write(I)V
return-void
.end method
@@ -1,16 +1,16 @@
.class public LTestBooleanToLong;
.class public Lconditions/TestBooleanToLong;
.super Ljava/lang/Object;
.field private showConsent:Z
.method public writeToParcel(LTestBooleanToLong;)V
.method public writeToParcel(Lconditions/TestBooleanToLong;)V
.locals 0
iget-boolean p1, p0, LTestBooleanToLong;->showConsent:Z
iget-boolean p1, p0, Lconditions/TestBooleanToLong;->showConsent:Z
int-to-long p1, p1
invoke-virtual {p0, p1}, LTestBooleanToLong;->write(J)V
invoke-virtual {p0, p1}, Lconditions/TestBooleanToLong;->write(J)V
return-void
.end method
@@ -1,16 +1,16 @@
.class public LTestBooleanToShort;
.class public Lconditions/TestBooleanToShort;
.super Ljava/lang/Object;
.field private showConsent:Z
.method public writeToParcel(LTestBooleanToShort;)V
.method public writeToParcel(Lconditions/TestBooleanToShort;)V
.locals 0
iget-boolean p1, p0, LTestBooleanToShort;->showConsent:Z
iget-boolean p1, p0, Lconditions/TestBooleanToShort;->showConsent:Z
int-to-short p1, p1
invoke-virtual {p0, p1}, LTestBooleanToShort;->write(S)V
invoke-virtual {p0, p1}, Lconditions/TestBooleanToShort;->write(S)V
return-void
.end method