fix: allow implicit type cast for array operations (#1407)
This commit is contained in:
@@ -389,11 +389,11 @@ public class InsnDecoder {
|
||||
return arrLenInsn;
|
||||
|
||||
case AGET:
|
||||
return arrayGet(insn, ArgType.INT_FLOAT);
|
||||
return arrayGet(insn, ArgType.INT_FLOAT, ArgType.NARROW_NUMBERS_NO_BOOL);
|
||||
case AGET_BOOLEAN:
|
||||
return arrayGet(insn, ArgType.BOOLEAN);
|
||||
case AGET_BYTE:
|
||||
return arrayGet(insn, ArgType.BYTE);
|
||||
return arrayGet(insn, ArgType.BYTE, ArgType.NARROW_INTEGRAL);
|
||||
case AGET_BYTE_BOOLEAN:
|
||||
return arrayGet(insn, ArgType.BYTE_BOOLEAN);
|
||||
case AGET_CHAR:
|
||||
@@ -406,7 +406,7 @@ public class InsnDecoder {
|
||||
return arrayGet(insn, ArgType.UNKNOWN_OBJECT);
|
||||
|
||||
case APUT:
|
||||
return arrayPut(insn, ArgType.INT_FLOAT);
|
||||
return arrayPut(insn, ArgType.INT_FLOAT, ArgType.NARROW_NUMBERS_NO_BOOL);
|
||||
case APUT_BOOLEAN:
|
||||
return arrayPut(insn, ArgType.BOOLEAN);
|
||||
case APUT_BYTE:
|
||||
@@ -607,16 +607,24 @@ public class InsnDecoder {
|
||||
}
|
||||
|
||||
private InsnNode arrayGet(InsnData insn, ArgType argType) {
|
||||
return arrayGet(insn, argType, argType);
|
||||
}
|
||||
|
||||
private InsnNode arrayGet(InsnData insn, ArgType arrElemType, ArgType resType) {
|
||||
InsnNode inode = new InsnNode(InsnType.AGET, 2);
|
||||
inode.setResult(InsnArg.typeImmutableIfKnownReg(insn, 0, argType));
|
||||
inode.addArg(InsnArg.typeImmutableIfKnownReg(insn, 1, ArgType.array(argType)));
|
||||
inode.setResult(InsnArg.typeImmutableIfKnownReg(insn, 0, resType));
|
||||
inode.addArg(InsnArg.typeImmutableIfKnownReg(insn, 1, ArgType.array(arrElemType)));
|
||||
inode.addArg(InsnArg.reg(insn, 2, ArgType.NARROW_INTEGRAL));
|
||||
return inode;
|
||||
}
|
||||
|
||||
private InsnNode arrayPut(InsnData insn, ArgType argType) {
|
||||
return arrayPut(insn, argType, argType);
|
||||
}
|
||||
|
||||
private InsnNode arrayPut(InsnData insn, ArgType arrElemType, ArgType argType) {
|
||||
InsnNode inode = new InsnNode(InsnType.APUT, 3);
|
||||
inode.addArg(InsnArg.typeImmutableIfKnownReg(insn, 1, ArgType.array(argType)));
|
||||
inode.addArg(InsnArg.typeImmutableIfKnownReg(insn, 1, ArgType.array(arrElemType)));
|
||||
inode.addArg(InsnArg.reg(insn, 2, ArgType.NARROW_INTEGRAL));
|
||||
inode.addArg(InsnArg.typeImmutableIfKnownReg(insn, 0, argType));
|
||||
return inode;
|
||||
|
||||
+36
@@ -87,6 +87,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
|
||||
this::tryDeduceTypes,
|
||||
this::trySplitConstInsns,
|
||||
this::tryToFixIncompatiblePrimitives,
|
||||
this::tryToForceImmutableTypes,
|
||||
this::tryInsertAdditionalMove,
|
||||
this::runMultiVariableSearch,
|
||||
this::tryRemoveGenerics);
|
||||
@@ -835,6 +836,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
|
||||
if (typeInfo.getType().isTypeKnown()) {
|
||||
return false;
|
||||
}
|
||||
boolean assigned = false;
|
||||
for (ITypeBound bound : typeInfo.getBounds()) {
|
||||
ArgType boundType = bound.getType();
|
||||
switch (bound.getBound()) {
|
||||
@@ -842,6 +844,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
|
||||
if (!boundType.contains(PrimitiveType.BOOLEAN)) {
|
||||
return false;
|
||||
}
|
||||
assigned = true;
|
||||
break;
|
||||
case USE:
|
||||
if (!boundType.canBeAnyNumber()) {
|
||||
@@ -850,6 +853,9 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!assigned) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean fixed = false;
|
||||
for (ITypeBound bound : typeInfo.getBounds()) {
|
||||
@@ -932,6 +938,36 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
|
||||
return convertInsn;
|
||||
}
|
||||
|
||||
private boolean tryToForceImmutableTypes(MethodNode mth) {
|
||||
boolean fixed = false;
|
||||
for (SSAVar ssaVar : mth.getSVars()) {
|
||||
ArgType type = ssaVar.getTypeInfo().getType();
|
||||
if (!type.isTypeKnown() && ssaVar.isTypeImmutable()) {
|
||||
if (forceImmutableType(ssaVar)) {
|
||||
fixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!fixed) {
|
||||
return false;
|
||||
}
|
||||
return runTypePropagation(mth);
|
||||
}
|
||||
|
||||
private boolean forceImmutableType(SSAVar ssaVar) {
|
||||
for (RegisterArg useArg : ssaVar.getUseList()) {
|
||||
InsnNode parentInsn = useArg.getParentInsn();
|
||||
if (parentInsn != null) {
|
||||
InsnType insnType = parentInsn.getType();
|
||||
if (insnType == InsnType.AGET || insnType == InsnType.APUT) {
|
||||
ssaVar.setType(ssaVar.getImmutableType());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void assignImmutableTypes(MethodNode mth) {
|
||||
for (SSAVar ssaVar : mth.getSVars()) {
|
||||
ArgType immutableType = getSsaImmutableType(ssaVar);
|
||||
|
||||
@@ -513,7 +513,18 @@ public final class TypeUpdate {
|
||||
|
||||
private TypeUpdateResult arrayGetListener(TypeUpdateInfo updateInfo, InsnNode insn, InsnArg arg, ArgType candidateType) {
|
||||
if (isAssign(insn, arg)) {
|
||||
return updateTypeChecked(updateInfo, insn.getArg(0), ArgType.array(candidateType));
|
||||
TypeUpdateResult result = updateTypeChecked(updateInfo, insn.getArg(0), ArgType.array(candidateType));
|
||||
if (result == REJECT) {
|
||||
ArgType arrType = insn.getArg(0).getType();
|
||||
if (arrType.isTypeKnown() && arrType.isArray() && arrType.getArrayElement().isPrimitive()) {
|
||||
TypeCompareEnum compResult = comparator.compareTypes(candidateType, arrType.getArrayElement());
|
||||
if (compResult == TypeCompareEnum.WIDER) {
|
||||
// allow implicit upcast for primitive types (int a = byteArr[n])
|
||||
return CHANGED;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
InsnArg arrArg = insn.getArg(0);
|
||||
if (arrArg == arg) {
|
||||
@@ -521,7 +532,18 @@ public final class TypeUpdate {
|
||||
if (arrayElement == null) {
|
||||
return REJECT;
|
||||
}
|
||||
return updateTypeChecked(updateInfo, insn.getResult(), arrayElement);
|
||||
TypeUpdateResult result = updateTypeChecked(updateInfo, insn.getResult(), arrayElement);
|
||||
if (result == REJECT) {
|
||||
ArgType resType = insn.getResult().getType();
|
||||
if (resType.isTypeKnown() && resType.isPrimitive()) {
|
||||
TypeCompareEnum compResult = comparator.compareTypes(resType, arrayElement);
|
||||
if (compResult == TypeCompareEnum.WIDER) {
|
||||
// allow implicit upcast for primitive types (int a = byteArr[n])
|
||||
return CHANGED;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// index argument
|
||||
return SAME;
|
||||
@@ -538,10 +560,10 @@ public final class TypeUpdate {
|
||||
TypeUpdateResult result = updateTypeChecked(updateInfo, putArg, arrayElement);
|
||||
if (result == REJECT) {
|
||||
ArgType putType = putArg.getType();
|
||||
if (putType.isTypeKnown() && !putType.isPrimitive()) {
|
||||
if (putType.isTypeKnown()) {
|
||||
TypeCompareEnum compResult = comparator.compareTypes(arrayElement, putType);
|
||||
if (compResult == TypeCompareEnum.WIDER || compResult == TypeCompareEnum.WIDER_BY_GENERIC) {
|
||||
// allow wider result (i.e allow put in Object[] any objects)
|
||||
// allow wider result (i.e. allow put any objects in Object[] or byte in int[])
|
||||
return CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package jadx.tests.integration.types;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.tests.api.SmaliTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
/**
|
||||
* Issue 1407
|
||||
*/
|
||||
public class TestTypeResolver19 extends SmaliTest {
|
||||
|
||||
public static class TestCls {
|
||||
public static int[] test(byte[] bArr) {
|
||||
int[] iArr = new int[bArr.length];
|
||||
for (int i = 0; i < bArr.length; i++) {
|
||||
iArr[i] = bArr[i];
|
||||
}
|
||||
return iArr;
|
||||
}
|
||||
|
||||
public static int[] test2(byte[] bArr) {
|
||||
int[] iArr = new int[bArr.length];
|
||||
for (int i = 0; i < bArr.length; i++) {
|
||||
int i2 = bArr[i];
|
||||
if (i2 < 0) {
|
||||
i2 = (int) ((long) i2 & 0xFFFF_FFFFL);
|
||||
}
|
||||
iArr[i] = i2;
|
||||
}
|
||||
return iArr;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
noDebugInfo();
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.containsOne("iArr[i] = bArr[i];")
|
||||
.containsOne("iArr[i] = i2;");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user